รวมภาพตั้งแต่ตอน Print
This commit is contained in:
@@ -8,6 +8,7 @@ const showResult = ref(false)
|
||||
const photos = ref<string[]>([])
|
||||
const layout = ref<'1x4' | '2x2'>('1x4')
|
||||
const frame = ref<number>(0)
|
||||
const finalImage = ref<string | null>(null)
|
||||
|
||||
onMounted(() => {
|
||||
// โหลดข้อมูลจาก localStorage
|
||||
@@ -29,6 +30,9 @@ onMounted(() => {
|
||||
|
||||
// เริ่มนับถอยหลัง
|
||||
startCountdown()
|
||||
|
||||
// Load the final image from localStorage
|
||||
finalImage.value = localStorage.getItem('photobooth-final-image');
|
||||
})
|
||||
|
||||
const startCountdown = () => {
|
||||
@@ -53,7 +57,7 @@ const getGridStyle = () => {
|
||||
gridTemplateColumns: '1fr',
|
||||
gridTemplateRows: 'repeat(4, 1fr)',
|
||||
gap: '8px',
|
||||
aspectRatio: '1/5' // ทำให้แต่ละภาพยาวขึ้น (1:2 aspect ratio รวม)
|
||||
aspectRatio: '1/1' // ทำให้แต่ละภาพยาวขึ้น (1:2 aspect ratio รวม)
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
@@ -65,6 +69,226 @@ const getGridStyle = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const saveToLocalStorage = (key: string, data: any) => {
|
||||
try {
|
||||
localStorage.setItem(key, JSON.stringify(data));
|
||||
} catch (error) {
|
||||
console.error('Error saving to localStorage:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const drawFrameBackground = (context: CanvasRenderingContext2D, width: number, height: number, frameType: number) => {
|
||||
context.clearRect(0, 0, width, height);
|
||||
|
||||
switch (frameType) {
|
||||
case 0: // Classic frame
|
||||
context.fillStyle = '#000000';
|
||||
context.fillRect(0, 0, width, height);
|
||||
break;
|
||||
case 1: // Modern frame
|
||||
const gradient1 = context.createLinearGradient(0, 0, width, height);
|
||||
gradient1.addColorStop(0, '#667eea');
|
||||
gradient1.addColorStop(1, '#764ba2');
|
||||
context.fillStyle = gradient1;
|
||||
context.fillRect(0, 0, width, height);
|
||||
break;
|
||||
case 2: // Vintage frame
|
||||
const gradient2 = context.createLinearGradient(0, 0, width, height);
|
||||
gradient2.addColorStop(0, '#f093fb');
|
||||
gradient2.addColorStop(1, '#f5576c');
|
||||
context.fillStyle = gradient2;
|
||||
context.fillRect(0, 0, width, height);
|
||||
break;
|
||||
case 3: // Colorful frame
|
||||
const gradient3 = context.createLinearGradient(0, 0, width, height);
|
||||
gradient3.addColorStop(0, '#4facfe');
|
||||
gradient3.addColorStop(1, '#00f2fe');
|
||||
context.fillStyle = gradient3;
|
||||
context.fillRect(0, 0, width, height);
|
||||
break;
|
||||
case 4: // Minimal frame
|
||||
context.fillStyle = '#f8f9fa';
|
||||
context.fillRect(0, 0, width, height);
|
||||
context.strokeStyle = '#dee2e6';
|
||||
context.lineWidth = 4;
|
||||
context.strokeRect(0, 0, width, height);
|
||||
break;
|
||||
default:
|
||||
console.warn('Unknown frame type:', frameType);
|
||||
context.fillStyle = '#ffffff';
|
||||
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 generateAndCacheImage = async () => {
|
||||
try {
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
if (!context) {
|
||||
throw new Error('Could not get canvas context')
|
||||
}
|
||||
|
||||
// ขนาดรูปแต่ละภาพ (360x480 = 3:4) - ลดขนาดลงเพื่อป้องกันปัญหา canvas ใหญ่เกินไป
|
||||
const photoWidth = 360
|
||||
const photoHeight = 480
|
||||
const gap = 20 // ระยะห่างระหว่างรูป
|
||||
|
||||
let canvasWidth, canvasHeight
|
||||
let headerHeight = 0, footerHeight = 0
|
||||
let sidePadding = 0
|
||||
|
||||
if (layout.value === '1x4') {
|
||||
// 1x4: 1 คอลัมน์ 4 แถว + header/footer
|
||||
headerHeight = 50
|
||||
footerHeight = 50
|
||||
sidePadding = 30 // padding ซ้ายขวาเพื่อให้ total width = 420
|
||||
canvasWidth = 420
|
||||
canvasHeight = headerHeight + (photoHeight * 4) + (gap * 3) + footerHeight
|
||||
} else {
|
||||
// 2x2: 2 คอลัมน์ 2 แถว + header/footer
|
||||
headerHeight = 60
|
||||
footerHeight = 60
|
||||
sidePadding = 20 // padding ซ้ายขวาเพื่อให้ total width = 780
|
||||
canvasWidth = 780
|
||||
canvasHeight = headerHeight + (photoHeight * 2) + gap + footerHeight
|
||||
}
|
||||
|
||||
console.log('Canvas dimensions:', canvasWidth, 'x', canvasHeight)
|
||||
canvas.width = canvasWidth
|
||||
canvas.height = canvasHeight
|
||||
|
||||
// วาด background ตาม frame
|
||||
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++) {
|
||||
const photoSrc = photos.value[i]
|
||||
if (!photoSrc) continue
|
||||
|
||||
const img = new Image()
|
||||
img.src = photoSrc
|
||||
|
||||
await new Promise((resolve) => {
|
||||
img.onload = () => {
|
||||
let x, y
|
||||
|
||||
if (layout.value === '1x4') {
|
||||
// 1x4 layout - วาดรูปตรงกลาง (มี padding ซ้ายขวา)
|
||||
x = sidePadding
|
||||
y = headerHeight + i * (photoHeight + gap)
|
||||
} else {
|
||||
// 2x2 layout - วาดรูปตรงกลาง (มี padding ซ้ายขวา)
|
||||
const col = i % 2
|
||||
const row = Math.floor(i / 2)
|
||||
x = sidePadding + col * (photoWidth + gap)
|
||||
y = headerHeight + row * (photoHeight + gap)
|
||||
}
|
||||
|
||||
// วาดรูปภาพ
|
||||
context.drawImage(img, x, y, photoWidth, photoHeight)
|
||||
resolve(void 0)
|
||||
}
|
||||
img.onerror = () => {
|
||||
console.error(`Failed to load image ${i}`)
|
||||
resolve(void 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// สร้าง PNG data URL และบันทึกใน localStorage
|
||||
const dataUrl = canvas.toDataURL('image/png')
|
||||
|
||||
try {
|
||||
saveToLocalStorage('photobooth-final-image', dataUrl);
|
||||
finalImage.value = dataUrl;
|
||||
} catch (error) {
|
||||
if (error instanceof DOMException && error.name === 'QuotaExceededError') {
|
||||
console.error('localStorage quota exceeded for cached image')
|
||||
alert('ไม่สามารถบันทึกภาพได้เนื่องจากพื้นที่เก็บข้อมูลไม่เพียงพอ')
|
||||
} else {
|
||||
console.error('Error saving cached image:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in generateAndCacheImage:', error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const compressImage = (dataUrl: string, quality: number): Promise<string> => {
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
img.src = dataUrl;
|
||||
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
if (!ctx) {
|
||||
console.error('Canvas context not available for compression');
|
||||
resolve(dataUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
resolve(canvas.toDataURL('image/jpeg', quality));
|
||||
};
|
||||
|
||||
img.onerror = () => {
|
||||
console.error('Failed to compress image');
|
||||
resolve(dataUrl);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// ...existing code...
|
||||
|
||||
generateAndCacheImage();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -86,14 +310,13 @@ const getGridStyle = () => {
|
||||
<p>กำลังเตรียมไฟล์สำหรับดาวน์โหลด...</p>
|
||||
</div>
|
||||
|
||||
<div class="photo-result">
|
||||
<div class="photo-result" style="display: none;">
|
||||
<div class="photo-frame" :class="`frame-${frame}`">
|
||||
<div class="photo-grid" :style="getGridStyle()">
|
||||
<div
|
||||
v-for="(photo, index) in photos"
|
||||
:key="index"
|
||||
class="photo-cell"
|
||||
>
|
||||
class="photo-cell" >
|
||||
<img :src="photo" :alt="`Photo ${index + 1}`" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -105,6 +328,14 @@ const getGridStyle = () => {
|
||||
<span>กำลังสร้างไฟล์...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showResult" class="final-image-section">
|
||||
<h2>ภาพที่รวมแล้ว</h2>
|
||||
<div class="final-image-container">
|
||||
<img :src="finalImage" alt="Final Combined Image" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -232,6 +463,26 @@ const getGridStyle = () => {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.final-image-section {
|
||||
margin-top: 2rem;
|
||||
text-align: center;
|
||||
animation: fadeIn 1s ease-out forwards;
|
||||
}
|
||||
|
||||
.final-image-container {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.final-image-container img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
|
||||
Reference in New Issue
Block a user