feat: optimize image handling by reducing canvas size and improving localStorage management
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, computed } from 'vue'
|
import { ref, onMounted, computed, nextTick } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const photos = ref<string[]>([])
|
const photos = ref<string[]>([])
|
||||||
const layout = ref<'1x4' | '2x2'>('1x4')
|
const layout = ref<'1x4' | '2x2'>('1x4')
|
||||||
const frame = ref<number>(0)
|
const frame = ref<number>(0)
|
||||||
const shareUrl = ref('')
|
const cachedImageUrl = ref('')
|
||||||
const qrCodeUrl = ref('')
|
const isGeneratingImage = ref(false)
|
||||||
|
const hasError = ref(false)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// โหลดข้อมูลจาก localStorage
|
// โหลดข้อมูลจาก localStorage
|
||||||
@@ -27,8 +28,8 @@ onMounted(() => {
|
|||||||
frame.value = parseInt(savedFrame)
|
frame.value = parseInt(savedFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
// สร้าง URL สำหรับแชร์ (ในโปรเจกต์จริงควรอัพโหลดขึ้น cloud)
|
// สร้าง PNG และบันทึกใน localStorage (ถ้ายังไม่มี)
|
||||||
generateShareUrl()
|
generateAndCacheImage()
|
||||||
})
|
})
|
||||||
|
|
||||||
const getGridStyle = computed(() => {
|
const getGridStyle = computed(() => {
|
||||||
@@ -38,7 +39,7 @@ const getGridStyle = computed(() => {
|
|||||||
gridTemplateColumns: '1fr',
|
gridTemplateColumns: '1fr',
|
||||||
gridTemplateRows: 'repeat(4, 1fr)',
|
gridTemplateRows: 'repeat(4, 1fr)',
|
||||||
gap: '8px',
|
gap: '8px',
|
||||||
aspectRatio: '1/2' // ทำให้แต่ละภาพยาวขึ้น (1:2 aspect ratio รวม)
|
aspectRatio: '1/1' // ทำให้แต่ละภาพยาวขึ้น (1:2 aspect ratio รวม)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
@@ -51,50 +52,50 @@ const getGridStyle = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const generateShareUrl = () => {
|
const generateAndCacheImage = async () => {
|
||||||
// ในโปรเจกต์จริง ควรอัพโหลดรูปขึ้น cloud และสร้าง URL
|
try {
|
||||||
// ตัวอย่างนี้ใช้ data URL ชั่วคราว
|
hasError.value = false
|
||||||
const photoData = {
|
if (photos.value.length === 0) return
|
||||||
photos: photos.value,
|
|
||||||
layout: layout.value,
|
|
||||||
frame: frame.value,
|
|
||||||
timestamp: Date.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
// เข้ารหัสข้อมูลเป็น base64 และสร้าง URL
|
isGeneratingImage.value = true
|
||||||
const encodedData = btoa(JSON.stringify(photoData))
|
|
||||||
shareUrl.value = `${window.location.origin}/share/${encodedData}`
|
|
||||||
|
|
||||||
// สร้าง QR code URL (ใช้ service ภายนอก)
|
// สร้าง key สำหรับ localStorage
|
||||||
qrCodeUrl.value = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(shareUrl.value)}`
|
const storageKey = `photobooth-${layout.value}-${frame.value}-${photos.value.length}`
|
||||||
}
|
|
||||||
|
|
||||||
const downloadImage = async () => {
|
// ตรวจสอบว่ามี PNG ที่สร้างไว้แล้วหรือไม่
|
||||||
if (photos.value.length === 0) return
|
const cachedImage = localStorage.getItem(storageKey)
|
||||||
|
if (cachedImage) {
|
||||||
|
cachedImageUrl.value = cachedImage
|
||||||
|
await nextTick()
|
||||||
|
return // มีแล้ว ไม่ต้องสร้างใหม่
|
||||||
|
}
|
||||||
|
|
||||||
// สร้าง canvas สำหรับรวมรูป
|
// สร้าง canvas สำหรับรวมรูป
|
||||||
const canvas = document.createElement('canvas')
|
const canvas = document.createElement('canvas')
|
||||||
const context = canvas.getContext('2d')
|
const context = canvas.getContext('2d')
|
||||||
|
|
||||||
if (!context) return
|
if (!context) {
|
||||||
|
throw new Error('Could not get canvas context')
|
||||||
|
}
|
||||||
|
|
||||||
// ขนาดรูปแต่ละภาพ (720x960 = 3:4)
|
// ขนาดรูปแต่ละภาพ (360x480 = 3:4) - ลดขนาดลงเพื่อป้องกันปัญหา canvas ใหญ่เกินไป
|
||||||
const photoWidth = 720
|
const photoWidth = 360
|
||||||
const photoHeight = 960
|
const photoHeight = 480
|
||||||
const gap = 20 // ระยะห่างระหว่างรูป
|
const gap = 20 // ระยะห่างระหว่างรูป
|
||||||
|
|
||||||
let canvasWidth, canvasHeight
|
let canvasWidth, canvasHeight
|
||||||
|
|
||||||
if (layout.value === '1x4') {
|
if (layout.value === '1x4') {
|
||||||
// 1x4: 1 คอลัมน์ 4 แถว
|
// 1x4: 1 คอลัมน์ 4 แถว
|
||||||
canvasWidth = photoWidth
|
canvasWidth = photoWidth + 40
|
||||||
canvasHeight = (photoHeight * 4) + (gap * 3)
|
canvasHeight = (photoHeight * 4) + (gap * 3) + 40
|
||||||
} else {
|
} else {
|
||||||
// 2x2: 2 คอลัมน์ 2 แถว
|
// 2x2: 2 คอลัมน์ 2 แถว
|
||||||
canvasWidth = (photoWidth * 2) + gap
|
canvasWidth = (photoWidth * 2) + gap + 40
|
||||||
canvasHeight = (photoHeight * 2) + gap
|
canvasHeight = (photoHeight * 2) + gap + 40
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('Canvas dimensions:', canvasWidth, 'x', canvasHeight)
|
||||||
canvas.width = canvasWidth
|
canvas.width = canvasWidth
|
||||||
canvas.height = canvasHeight
|
canvas.height = canvasHeight
|
||||||
|
|
||||||
@@ -103,8 +104,11 @@ const downloadImage = async () => {
|
|||||||
|
|
||||||
// วาดรูปภาพ
|
// วาดรูปภาพ
|
||||||
for (let i = 0; i < photos.value.length; i++) {
|
for (let i = 0; i < photos.value.length; i++) {
|
||||||
|
const photoSrc = photos.value[i]
|
||||||
|
if (!photoSrc) continue
|
||||||
|
|
||||||
const img = new Image()
|
const img = new Image()
|
||||||
img.src = photos.value[i]
|
img.src = photoSrc
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
@@ -126,22 +130,57 @@ const downloadImage = async () => {
|
|||||||
context.drawImage(img, x, y, photoWidth, photoHeight)
|
context.drawImage(img, x, y, photoWidth, photoHeight)
|
||||||
resolve(void 0)
|
resolve(void 0)
|
||||||
}
|
}
|
||||||
|
img.onerror = () => {
|
||||||
|
console.error(`Failed to load image ${i}`)
|
||||||
|
resolve(void 0)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// สร้าง PNG และ download
|
// สร้าง PNG data URL และบันทึกใน localStorage
|
||||||
canvas.toBlob((blob) => {
|
const dataUrl = canvas.toDataURL('image/png')
|
||||||
if (blob) {
|
|
||||||
const url = URL.createObjectURL(blob)
|
try {
|
||||||
const a = document.createElement('a')
|
localStorage.setItem(storageKey, dataUrl)
|
||||||
a.href = url
|
cachedImageUrl.value = dataUrl
|
||||||
a.download = `photobooth-${Date.now()}.png`
|
// ให้ Vue reactive update ก่อน
|
||||||
document.body.appendChild(a)
|
await nextTick()
|
||||||
a.click()
|
} catch (error) {
|
||||||
document.body.removeChild(a)
|
if (error instanceof DOMException && error.name === 'QuotaExceededError') {
|
||||||
URL.revokeObjectURL(url)
|
console.error('localStorage quota exceeded for cached image')
|
||||||
|
alert('ไม่สามารถบันทึกภาพได้เนื่องจากพื้นที่เก็บข้อมูลไม่เพียงพอ')
|
||||||
|
} else {
|
||||||
|
console.error('Error saving cached image:', error)
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
}, 'image/png')
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in generateAndCacheImage:', error)
|
||||||
|
hasError.value = true
|
||||||
|
} finally {
|
||||||
|
isGeneratingImage.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadImage = async () => {
|
||||||
|
if (photos.value.length === 0) return
|
||||||
|
|
||||||
|
// ตรวจสอบว่า cachedImageUrl มีหรือไม่ ถ้าไม่มีให้สร้างก่อน
|
||||||
|
if (!cachedImageUrl.value) {
|
||||||
|
await generateAndCacheImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download จาก cachedImageUrl
|
||||||
|
downloadFromDataUrl(cachedImageUrl.value, `photobooth-${Date.now()}.png`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadFromDataUrl = (dataUrl: string, filename: string) => {
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.href = dataUrl
|
||||||
|
a.download = filename
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
document.body.removeChild(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
const drawFrameBackground = (context: CanvasRenderingContext2D, width: number, height: number, frameType: number) => {
|
const drawFrameBackground = (context: CanvasRenderingContext2D, width: number, height: number, frameType: number) => {
|
||||||
@@ -178,29 +217,6 @@ const drawFrameBackground = (context: CanvasRenderingContext2D, width: number, h
|
|||||||
context.fillRect(0, 0, width, height)
|
context.fillRect(0, 0, width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
const shareImage = async () => {
|
|
||||||
if (navigator.share) {
|
|
||||||
try {
|
|
||||||
await navigator.share({
|
|
||||||
title: 'รูปถ่ายจาก DekThai Photobooth',
|
|
||||||
text: 'ดูรูปถ่ายสุดพิเศษที่ฉันสร้าง!',
|
|
||||||
url: shareUrl.value
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.log('Error sharing:', error)
|
|
||||||
copyToClipboard()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
copyToClipboard()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const copyToClipboard = () => {
|
|
||||||
navigator.clipboard.writeText(shareUrl.value).then(() => {
|
|
||||||
alert('คัดลอกลิงก์แล้ว! สามารถแชร์ให้เพื่อนได้')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const startOver = () => {
|
const startOver = () => {
|
||||||
// เคลียร์ข้อมูลและกลับไปหน้าแรก
|
// เคลียร์ข้อมูลและกลับไปหน้าแรก
|
||||||
localStorage.removeItem('photobooth-photos')
|
localStorage.removeItem('photobooth-photos')
|
||||||
@@ -220,14 +236,14 @@ const startOver = () => {
|
|||||||
<section class="result-section">
|
<section class="result-section">
|
||||||
<div class="photo-result">
|
<div class="photo-result">
|
||||||
<div class="photo-frame" :class="`frame-${frame}`">
|
<div class="photo-frame" :class="`frame-${frame}`">
|
||||||
<div class="photo-grid" :style="getGridStyle">
|
<div v-if="isGeneratingImage && !cachedImageUrl" class="loading-indicator">
|
||||||
<div
|
<div class="spinner"></div>
|
||||||
v-for="(photo, index) in photos"
|
<p>กำลังสร้างภาพ...</p>
|
||||||
:key="index"
|
</div>
|
||||||
class="photo-cell"
|
|
||||||
>
|
<img v-else-if="cachedImageUrl" :src="cachedImageUrl" alt="Combined Photo" class="combined-image" />
|
||||||
<img :src="photo" :alt="`Photo ${index + 1}`" />
|
<div v-else-if="hasError" class="error-message">
|
||||||
</div>
|
<p>ไม่สามารถสร้างภาพได้</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -239,22 +255,6 @@ const startOver = () => {
|
|||||||
<span class="button-icon">📥</span>
|
<span class="button-icon">📥</span>
|
||||||
ดาวน์โหลด
|
ดาวน์โหลด
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button @click="shareImage" class="share-button">
|
|
||||||
<span class="button-icon">📤</span>
|
|
||||||
แชร์
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="qr-section">
|
|
||||||
<h3>สแกน QR Code เพื่อดูรูป</h3>
|
|
||||||
<div class="qr-container">
|
|
||||||
<img :src="qrCodeUrl" alt="QR Code" class="qr-code" />
|
|
||||||
<p class="qr-text">{{ shareUrl }}</p>
|
|
||||||
<button @click="copyToClipboard" class="copy-link-button">
|
|
||||||
คัดลอกลิงก์
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -328,6 +328,46 @@ const startOver = () => {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.combined-image {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-indicator {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 2rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: 4px solid #f3f3f3;
|
||||||
|
border-top: 4px solid #007bff;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 2rem;
|
||||||
|
color: #dc3545;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.actions-section {
|
.actions-section {
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
@@ -338,7 +378,7 @@ const startOver = () => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.download-button, .share-button {
|
.download-button {
|
||||||
background: #007bff;
|
background: #007bff;
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -354,77 +394,16 @@ const startOver = () => {
|
|||||||
box-shadow: 0 4px 15px rgba(0,123,255,0.3);
|
box-shadow: 0 4px 15px rgba(0,123,255,0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.share-button {
|
|
||||||
background: #28a745;
|
|
||||||
box-shadow: 0 4px 15px rgba(40,167,69,0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.download-button:hover {
|
.download-button:hover {
|
||||||
background: #0056b3;
|
background: #0056b3;
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 6px 20px rgba(0,123,255,0.4);
|
box-shadow: 0 6px 20px rgba(0,123,255,0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.share-button:hover {
|
|
||||||
background: #218838;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 6px 20px rgba(40,167,69,0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-icon {
|
.button-icon {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-section {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qr-section h3 {
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qr-container {
|
|
||||||
background: white;
|
|
||||||
padding: 2rem;
|
|
||||||
border-radius: 16px;
|
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
||||||
max-width: 300px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qr-code {
|
|
||||||
width: 150px;
|
|
||||||
height: 150px;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qr-text {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: #666;
|
|
||||||
word-break: break-all;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
padding: 0.5rem;
|
|
||||||
background: #f8f9fa;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-link-button {
|
|
||||||
background: #6c757d;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border-radius: 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-link-button:hover {
|
|
||||||
background: #5a6268;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-section {
|
.footer-section {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
@@ -449,10 +428,6 @@ const startOver = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Frame styles */
|
/* Frame styles */
|
||||||
.frame-0 {
|
|
||||||
/* Classic frame - default white */
|
|
||||||
}
|
|
||||||
|
|
||||||
.frame-1 {
|
.frame-1 {
|
||||||
/* Modern frame */
|
/* Modern frame */
|
||||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
background: linear-gradient(45deg, #667eea, #764ba2);
|
||||||
|
|||||||
@@ -81,9 +81,9 @@ const captureSinglePhoto = () => {
|
|||||||
cropY = (videoHeight - cropHeight) / 2
|
cropY = (videoHeight - cropHeight) / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// ตั้งค่าขนาด canvas เป็น 3:4 (720x960)
|
// ตั้งค่าขนาด canvas เป็น 3:4 (360x480) - ลดขนาดเพื่อป้องกัน localStorage quota exceeded
|
||||||
const finalWidth = 720
|
const finalWidth = 360
|
||||||
const finalHeight = 960
|
const finalHeight = 480
|
||||||
|
|
||||||
canvas.width = finalWidth
|
canvas.width = finalWidth
|
||||||
canvas.height = finalHeight
|
canvas.height = finalHeight
|
||||||
@@ -95,8 +95,8 @@ const captureSinglePhoto = () => {
|
|||||||
0, 0, finalWidth, finalHeight // ตำแหน่งและขนาดใน canvas
|
0, 0, finalWidth, finalHeight // ตำแหน่งและขนาดใน canvas
|
||||||
)
|
)
|
||||||
|
|
||||||
// แปลงเป็น base64
|
// แปลงเป็น base64 ด้วย quality 0.7 เพื่อลดขนาด
|
||||||
const photoDataUrl = canvas.toDataURL('image/jpeg', 0.9)
|
const photoDataUrl = canvas.toDataURL('image/jpeg', 0.7)
|
||||||
photos.value.push(photoDataUrl)
|
photos.value.push(photoDataUrl)
|
||||||
currentPhotoIndex.value++
|
currentPhotoIndex.value++
|
||||||
|
|
||||||
@@ -145,10 +145,30 @@ const startAutoCapture = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const proceedToNext = () => {
|
const proceedToNext = () => {
|
||||||
// เก็บรูปภาพไว้ใน localStorage
|
try {
|
||||||
localStorage.setItem('photobooth-photos', JSON.stringify(photos.value))
|
// เคลียร์ cached images ที่เก่าออกก่อนเพื่อให้มีพื้นที่
|
||||||
stopCamera()
|
const keysToRemove = []
|
||||||
router.push('/printstep')
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
const key = localStorage.key(i)
|
||||||
|
if (key && key.startsWith('photobooth-1x4-') || key.startsWith('photobooth-2x2-')) {
|
||||||
|
keysToRemove.push(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keysToRemove.forEach(key => localStorage.removeItem(key))
|
||||||
|
|
||||||
|
// เก็บรูปภาพไว้ใน localStorage
|
||||||
|
localStorage.setItem('photobooth-photos', JSON.stringify(photos.value))
|
||||||
|
stopCamera()
|
||||||
|
router.push('/printstep')
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof DOMException && error.name === 'QuotaExceededError') {
|
||||||
|
alert('รูปภาพมีขนาดใหญ่เกินไป กรุณาลองใหม่อีกครั้งหรือใช้รูปภาพขนาดเล็กลง')
|
||||||
|
console.error('localStorage quota exceeded:', error)
|
||||||
|
} else {
|
||||||
|
console.error('Error saving photos:', error)
|
||||||
|
alert('เกิดข้อผิดพลาดในการบันทึกภาพ')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ const cropImageTo34 = (imageSrc: string): Promise<string> => {
|
|||||||
cropY = (imgHeight - cropHeight) / 2
|
cropY = (imgHeight - cropHeight) / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// ตั้งค่าขนาด canvas เป็น 3:4 (720x960)
|
// ตั้งค่าขนาด canvas เป็น 3:4 (360x480) - ลดขนาดเพื่อป้องกัน localStorage quota exceeded
|
||||||
const finalWidth = 720
|
const finalWidth = 360
|
||||||
const finalHeight = 960
|
const finalHeight = 480
|
||||||
|
|
||||||
canvas.width = finalWidth
|
canvas.width = finalWidth
|
||||||
canvas.height = finalHeight
|
canvas.height = finalHeight
|
||||||
@@ -55,8 +55,8 @@ const cropImageTo34 = (imageSrc: string): Promise<string> => {
|
|||||||
0, 0, finalWidth, finalHeight // ตำแหน่งและขนาดใน canvas
|
0, 0, finalWidth, finalHeight // ตำแหน่งและขนาดใน canvas
|
||||||
)
|
)
|
||||||
|
|
||||||
// แปลงเป็น base64
|
// แปลงเป็น base64 ด้วย quality 0.7 เพื่อลดขนาด
|
||||||
const croppedDataUrl = canvas.toDataURL('image/jpeg', 0.9)
|
const croppedDataUrl = canvas.toDataURL('image/jpeg', 0.7)
|
||||||
resolve(croppedDataUrl)
|
resolve(croppedDataUrl)
|
||||||
}
|
}
|
||||||
img.src = imageSrc
|
img.src = imageSrc
|
||||||
@@ -103,9 +103,29 @@ const proceedToNext = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// เก็บรูปภาพไว้ใน localStorage
|
try {
|
||||||
localStorage.setItem('photobooth-photos', JSON.stringify(uploadedPhotos.value))
|
// เคลียร์ cached images ที่เก่าออกก่อนเพื่อให้มีพื้นที่
|
||||||
router.push('/printstep')
|
const keysToRemove = []
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
const key = localStorage.key(i)
|
||||||
|
if (key && (key.startsWith('photobooth-1x4-') || key.startsWith('photobooth-2x2-'))) {
|
||||||
|
keysToRemove.push(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keysToRemove.forEach(key => localStorage.removeItem(key))
|
||||||
|
|
||||||
|
// เก็บรูปภาพไว้ใน localStorage
|
||||||
|
localStorage.setItem('photobooth-photos', JSON.stringify(uploadedPhotos.value))
|
||||||
|
router.push('/printstep')
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof DOMException && error.name === 'QuotaExceededError') {
|
||||||
|
alert('รูปภาพมีขนาดใหญ่เกินไป กรุณาเลือกภาพขนาดเล็กลงหรือจำนวนน้อยลง')
|
||||||
|
console.error('localStorage quota exceeded:', error)
|
||||||
|
} else {
|
||||||
|
console.error('Error saving photos:', error)
|
||||||
|
alert('เกิดข้อผิดพลาดในการบันทึกภาพ')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user