feat: add header and footer images for 1x4 and 2x2 layouts, update canvas dimensions accordingly
BIN
public/Logo-200x60.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/assets/footer-1x4.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
4
public/assets/footer-1x4.svg
Normal 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 |
BIN
public/assets/footer-2x2.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
4
public/assets/footer-2x2.svg
Normal 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 |
BIN
public/assets/header-1x4.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
4
public/assets/header-1x4.svg
Normal 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 |
BIN
public/assets/header-2x2.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
4
public/assets/header-2x2.svg
Normal 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 |
@@ -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
|
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/footer-2x2.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
src/assets/header-1x4.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/assets/header-2x2.png
Normal 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
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||