feat: add header and footer images for 1x4 and 2x2 layouts, update canvas dimensions accordingly

This commit is contained in:
mrkad@rpi
2026-01-17 22:35:08 +07:00
parent 0d6d08f952
commit 2891ed9620
16 changed files with 77 additions and 18 deletions

BIN
public/Logo-200x60.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,4 @@
<svg width="420" height="50" xmlns="http://www.w3.org/2000/svg">
<rect width="420" height="50" fill="#dc3545"/>
<text x="210" y="30" text-anchor="middle" fill="white" font-family="Arial" font-size="16" font-weight="bold">Footer 1x4</text>
</svg>

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,4 @@
<svg width="780" height="60" xmlns="http://www.w3.org/2000/svg">
<rect width="780" height="60" fill="#ffc107"/>
<text x="390" y="35" text-anchor="middle" fill="black" font-family="Arial" font-size="18" font-weight="bold">Footer 2x2</text>
</svg>

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,4 @@
<svg width="420" height="50" xmlns="http://www.w3.org/2000/svg">
<rect width="420" height="50" fill="#007bff"/>
<text x="210" y="30" text-anchor="middle" fill="white" font-family="Arial" font-size="16" font-weight="bold">Header 1x4</text>
</svg>

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,4 @@
<svg width="780" height="60" xmlns="http://www.w3.org/2000/svg">
<rect width="780" height="60" fill="#28a745"/>
<text x="390" y="35" text-anchor="middle" fill="white" font-family="Arial" font-size="18" font-weight="bold">Header 2x2</text>
</svg>

After

Width:  |  Height:  |  Size: 249 B

View File

@@ -4,7 +4,7 @@ import HelloWorld from './components/HelloWorld.vue'
</script> </script>
<template> <template>
<header> <!-- <header >
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" /> <img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<div class="wrapper"> <div class="wrapper">
@@ -15,8 +15,9 @@ import HelloWorld from './components/HelloWorld.vue'
<RouterLink to="/about">About</RouterLink> <RouterLink to="/about">About</RouterLink>
</nav> </nav>
</div> </div>
</header> </header> -->
<RouterLink to="/">Home</RouterLink>
<RouterView /> <RouterView />
</template> </template>

BIN
src/assets/footer-1x4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
src/assets/footer-2x2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/assets/header-1x4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
src/assets/header-2x2.png Normal file

Binary file not shown.

View File

@@ -69,7 +69,7 @@ const goBack = () => {
</div> </div>
</section> </section>
<section class="frame-section"> <section class="frame-section" style="display: none;">
<h2>เลอกกรอบ</h2> <h2>เลอกกรอบ</h2>
<div class="frame-options"> <div class="frame-options">
<div <div

View File

@@ -84,15 +84,23 @@ const generateAndCacheImage = async () => {
const gap = 20 // ระยะห่างระหว่างรูป const gap = 20 // ระยะห่างระหว่างรูป
let canvasWidth, canvasHeight let canvasWidth, canvasHeight
let headerHeight = 0, footerHeight = 0
let sidePadding = 0
if (layout.value === '1x4') { if (layout.value === '1x4') {
// 1x4: 1 คอลัมน์ 4 แถว // 1x4: 1 คอลัมน์ 4 แถว + header/footer
canvasWidth = photoWidth + 40 headerHeight = 50
canvasHeight = (photoHeight * 4) + (gap * 3) + 40 footerHeight = 50
sidePadding = 30 // padding ซ้ายขวาเพื่อให้ total width = 420
canvasWidth = 420
canvasHeight = headerHeight + (photoHeight * 4) + (gap * 3) + footerHeight
} else { } else {
// 2x2: 2 คอลัมน์ 2 แถว // 2x2: 2 คอลัมน์ 2 แถว + header/footer
canvasWidth = (photoWidth * 2) + gap + 40 headerHeight = 60
canvasHeight = (photoHeight * 2) + gap + 40 footerHeight = 60
sidePadding = 20 // padding ซ้ายขวาเพื่อให้ total width = 780
canvasWidth = 780
canvasHeight = headerHeight + (photoHeight * 2) + gap + footerHeight
} }
console.log('Canvas dimensions:', canvasWidth, 'x', canvasHeight) console.log('Canvas dimensions:', canvasWidth, 'x', canvasHeight)
@@ -102,7 +110,15 @@ const generateAndCacheImage = async () => {
// วาด background ตาม frame // วาด background ตาม frame
drawFrameBackground(context, canvasWidth, canvasHeight, frame.value) drawFrameBackground(context, canvasWidth, canvasHeight, frame.value)
// วาดรูปภาพ // วาด header image
const headerImagePath = layout.value === '1x4' ? '/assets/header-1x4.png' : '/assets/header-2x2.png'
await drawHeaderFooterImage(context, headerImagePath, 0, 0, canvasWidth, headerHeight)
// วาด footer image
const footerImagePath = layout.value === '1x4' ? '/assets/footer-1x4.png' : '/assets/footer-2x2.png'
await drawHeaderFooterImage(context, footerImagePath, 0, canvasHeight - footerHeight, canvasWidth, footerHeight)
// วาดรูปภาพ (มี padding ซ้ายขวา)
for (let i = 0; i < photos.value.length; i++) { for (let i = 0; i < photos.value.length; i++) {
const photoSrc = photos.value[i] const photoSrc = photos.value[i]
if (!photoSrc) continue if (!photoSrc) continue
@@ -115,15 +131,15 @@ const generateAndCacheImage = async () => {
let x, y let x, y
if (layout.value === '1x4') { if (layout.value === '1x4') {
// 1x4 layout // 1x4 layout - วาดรูปตรงกลาง (มี padding ซ้ายขวา)
x = 0 x = sidePadding
y = i * (photoHeight + gap) y = headerHeight + i * (photoHeight + gap)
} else { } else {
// 2x2 layout // 2x2 layout - วาดรูปตรงกลาง (มี padding ซ้ายขวา)
const col = i % 2 const col = i % 2
const row = Math.floor(i / 2) const row = Math.floor(i / 2)
x = col * (photoWidth + gap) x = sidePadding + col * (photoWidth + gap)
y = row * (photoHeight + gap) y = headerHeight + row * (photoHeight + gap)
} }
// วาดรูปภาพ // วาดรูปภาพ
@@ -187,7 +203,7 @@ const drawFrameBackground = (context: CanvasRenderingContext2D, width: number, h
// วาด background ตาม frame type // วาด background ตาม frame type
switch (frameType) { switch (frameType) {
case 0: // Classic - white case 0: // Classic - white
context.fillStyle = '#ffffff' context.fillStyle = '#000000'
break break
case 1: // Modern - gradient case 1: // Modern - gradient
const gradient1 = context.createLinearGradient(0, 0, width, height) const gradient1 = context.createLinearGradient(0, 0, width, height)
@@ -211,12 +227,38 @@ const drawFrameBackground = (context: CanvasRenderingContext2D, width: number, h
context.fillStyle = '#f8f9fa' context.fillStyle = '#f8f9fa'
break break
default: default:
context.fillStyle = '#ffffff' context.fillStyle = '#000000'
} }
context.fillRect(0, 0, width, height) context.fillRect(0, 0, width, height)
} }
const drawHeaderFooterImage = async (context: CanvasRenderingContext2D, imagePath: string, x: number, y: number, width: number, height: number) => {
return new Promise<void>((resolve) => {
const img = new Image()
img.onload = () => {
context.drawImage(img, x, y, width, height)
resolve()
}
img.onerror = () => {
console.warn(`Failed to load header/footer image: ${imagePath}, drawing placeholder`)
// วาด placeholder color แทน
if (imagePath.includes('header-1x4')) {
context.fillStyle = '#007bff' // น้ำเงินสำหรับ 1x4 header
} else if (imagePath.includes('footer-1x4')) {
context.fillStyle = '#dc3545' // แดงสำหรับ 1x4 footer
} else if (imagePath.includes('header-2x2')) {
context.fillStyle = '#28a745' // เขียวสำหรับ 2x2 header
} else if (imagePath.includes('footer-2x2')) {
context.fillStyle = '#ffc107' // เหลืองสำหรับ 2x2 footer
}
context.fillRect(x, y, width, height)
resolve()
}
img.src = imagePath
})
}
const startOver = () => { const startOver = () => {
// เคลียร์ข้อมูลและกลับไปหน้าแรก // เคลียร์ข้อมูลและกลับไปหน้าแรก
localStorage.removeItem('photobooth-photos') localStorage.removeItem('photobooth-photos')