無線實物投影機

免安裝、免伺服器・手機秒變高畫質教學實物提示機

請選擇這台裝置的角色

在教室電腦開啟「接收投影畫面」,再用手機掃描畫面上的 QR Code 或輸入配對碼即可開始投影。

// ========================================== // 接收端 (Receiver) 互動功能完整繫結 // ========================================== let isFrozen = false; let drawMode = false; let currentPenColor = '#ef4444'; let currentPenSize = 4; let isDrawing = false; let lastX = 0; let lastY = 0; // 1. 定格 / 凍結畫面按鈕 document.getElementById('rx-btn-freeze').addEventListener('click', () => { const video = document.getElementById('rx-video'); const freezeCanvas = document.getElementById('rx-freeze-canvas'); const freezeBadge = document.getElementById('rx-freeze-badge'); const freezeLabel = document.getElementById('rx-freeze-label'); isFrozen = !isFrozen; if (isFrozen) { freezeCanvas.width = video.videoWidth || video.clientWidth; freezeCanvas.height = video.videoHeight || video.clientHeight; const ctx = freezeCanvas.getContext('2d'); ctx.drawImage(video, 0, 0, freezeCanvas.width, freezeCanvas.height); freezeCanvas.classList.remove('hidden'); freezeBadge.classList.remove('hidden'); freezeBadge.classList.add('flex'); freezeLabel.textContent = '解除定格'; showToast('畫面已定格凍結'); } else { freezeCanvas.classList.add('hidden'); freezeBadge.classList.add('hidden'); freezeBadge.classList.remove('flex'); freezeLabel.textContent = '定格'; showToast('已恢復即時投影'); } }); // 2. 教學畫筆按鈕 document.getElementById('rx-btn-draw').addEventListener('click', () => { drawMode = !drawMode; const palette = document.getElementById('rx-pen-palette'); const drawCanvas = document.getElementById('rx-draw-canvas'); if (drawMode) { palette.classList.remove('hidden'); drawCanvas.classList.add('cursor-crosshair-pen'); showToast('已開啟畫筆標註模式'); } else { palette.classList.add('hidden'); drawCanvas.classList.remove('cursor-crosshair-pen'); showToast('已關閉畫筆模式'); } }); // 畫筆顏色與粗細選擇 document.querySelectorAll('.pen-color-btn').forEach(btn => { btn.addEventListener('click', (e) => { document.querySelectorAll('.pen-color-btn').forEach(b => b.classList.remove('ring-2', 'ring-white', 'scale-110')); e.currentTarget.classList.add('ring-2', 'ring-white', 'scale-110'); currentPenColor = e.currentTarget.getAttribute('data-color'); }); }); // 畫布繪圖事件綁定 const drawCanvas = document.getElementById('rx-draw-canvas'); const drawCtx = drawCanvas.getContext('2d'); function getCanvasPos(e) { const rect = drawCanvas.getBoundingClientRect(); const clientX = e.touches ? e.touches[0].clientX : e.clientX; const clientY = e.touches ? e.touches[0].clientY : e.clientY; return { x: (clientX - rect.left) * (drawCanvas.width / rect.width), y: (clientY - rect.top) * (drawCanvas.height / rect.height) }; } drawCanvas.addEventListener('mousedown', startDraw); drawCanvas.addEventListener('touchstart', startDraw, { passive: false }); drawCanvas.addEventListener('mousemove', drawing); drawCanvas.addEventListener('touchmove', drawing, { passive: false }); window.addEventListener('mouseup', stopDraw); window.addEventListener('touchend', stopDraw); function startDraw(e) { if (!drawMode) return; e.preventDefault(); isDrawing = true; const pos = getCanvasPos(e); lastX = pos.x; lastY = pos.y; } function drawing(e) { if (!isDrawing || !drawMode) return; e.preventDefault(); const pos = getCanvasPos(e); drawCtx.beginPath(); drawCtx.moveTo(lastX, lastY); drawCtx.lineTo(pos.x, pos.y); drawCtx.strokeStyle = currentPenColor; drawCtx.lineWidth = currentPenSize; drawCtx.lineCap = 'round'; drawCtx.lineJoin = 'round'; drawCtx.stroke(); lastX = pos.x; lastY = pos.y; } function stopDraw() { isDrawing = false; } // 清除畫筆 document.getElementById('rx-btn-clear-draw').addEventListener('click', () => { drawCtx.clearRect(0, 0, drawCanvas.width, drawCanvas.height); showToast('已清除所有畫筆標註'); }); // 3. 截圖下載按鈕 document.getElementById('rx-btn-snapshot').addEventListener('click', () => { const video = document.getElementById('rx-video'); const width = video.videoWidth || 1280; const height = video.videoHeight || 720; const tempCanvas = document.createElement('canvas'); tempCanvas.width = width; tempCanvas.height = height; const ctx = tempCanvas.getContext('2d'); // 組合視訊與畫筆內容 ctx.drawImage(video, 0, 0, width, height); ctx.drawImage(drawCanvas, 0, 0, width, height); const link = document.createElement('a'); link.download = `實物投影截圖_${new Date().toISOString().slice(0, 10)}_${Date.now()}.png`; link.href = tempCanvas.toDataURL('image/png'); link.click(); showToast('已成功下載畫面截圖!'); });