"use strict";
const GREEN_FRAME_ANIMATION_DURATION = 4000;
$(document).ready(function () {
const videoElement = $('#videoElement')[0];
const canvas1 = $('#canvas1')[0];
const context1 = canvas1.getContext('2d');
let canvas2 = null;
let context2 = null;
const yellowFrame = $('#yellowFrame');
const greenFrame = $('#greenFrame');
const cameraSelect = $('#cameraSelect');
const projectorButton = $('#projectorButton');
const DRAGMODE_NONE = 0;
const DRAGMODE_SELECT = 1;
const DRAGMODE_MOVE = 2;
let currentStream = null;
let dragMode = DRAGMODE_NONE;
let startX, startY;
let isYellowFrameActive = false;
let greenFrameAnimationEndTime = 0;
// 全画面から開始する
greenFrame.css({ left: 0, top: 0, width: canvas1.width, height: canvas1.height, display: 'block' });
async function getCameras() {
const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = devices.filter(device => device.kind === 'videoinput');
cameraSelect.append(videoDevices.map(device => $('', { value: device.deviceId, text: device.label || `Camera` })));
}
async function updateStream(deviceId) {
if (currentStream) {
currentStream.getTracks().forEach(track => track.stop());
}
const stream = await navigator.mediaDevices.getUserMedia({
video: {
deviceId: deviceId ? { exact: deviceId } : undefined,
width: { min: 1920, ideal: 3840 },
height: { min: 1080, ideal: 2160 },
}
});
currentStream = stream;
videoElement.srcObject = stream;
}
cameraSelect.on('change', function () {
updateStream(this.value);
});
$('#videoElement').on('loadedmetadata', function () {
console.log(videoElement.videoWidth, videoElement.videoHeight);
setFrameAspectRatio(yellowFrame);
setFrameAspectRatio(greenFrame);
}).on('loadeddata', function () {
draw();
});
getCameras().then(() => {
if (cameraSelect.length > 0) {
updateStream(cameraSelect.val());
}
});
function setFrameAspectRatio(frame) {
let videoRatio = videoElement.videoWidth / videoElement.videoHeight;
frame.css('aspect-ratio', videoRatio);
}
$('#preview').on('mousedown', function (event) {
if (!isYellowFrameActive) {
startX = event.offsetX;
startY = event.offsetY;
yellowFrame.css({ left: startX, top: startY, display: 'block' });
dragMode = DRAGMODE_SELECT;
} else {
// 黄色の枠の外側をクリックした場合は、黄色の枠をリセットしてからドラッグ処理を開始
if (event.target === yellowFrame[0]) {
startX = event.offsetX;
startY = event.offsetY;
dragMode = DRAGMODE_SELECT;
} else {
// 黄色の枠の内側をクリックした場合は、黄色の枠を移動する
startX = yellowFrame.position().left;
startY = yellowFrame.position().top;
yellowFrame.data('offsetX', event.offsetX);
yellowFrame.data('offsetY', event.offsetY);
dragMode = DRAGMODE_MOVE;
}
}
});
$('body').on('mousemove', function (event) {
// targetがcanvas1では無い場合は、canvas1に対する座標に変換
if (event.target !== canvas1) {
let rect = canvas1.getBoundingClientRect();
event.offsetX = (event.clientX - rect.left) / rect.width * canvas1.width;
event.offsetY = (event.clientY - rect.top) / rect.height * canvas1.height;
}
if (dragMode === DRAGMODE_SELECT) {
updateFramePosition(yellowFrame, startX, startY, event.offsetX, event.offsetY);
greenFrameAnimationEndTime = Date.now() + GREEN_FRAME_ANIMATION_DURATION;
} else if (dragMode === DRAGMODE_MOVE) {
let offsetX = event.offsetX - yellowFrame.data('offsetX');
let offsetY = event.offsetY - yellowFrame.data('offsetY');
console.log(offsetX, offsetY);
let left = startX + offsetX;
let top = startY + offsetY;
if (left < 0) left = 0;
if (top < 0) top = 0;
if (left + yellowFrame.width() > canvas1.width) left = canvas1.width - yellowFrame.width();
if (top + yellowFrame.height() > canvas1.height) top = canvas1.height - yellowFrame.height();
yellowFrame.css({ left: left, top: top });
//updateFramePosition(yellowFrame, startX + offsetX, startY + offsetY, startX + offsetX + yellowFrame.width(), startY + offsetY + yellowFrame.height());
greenFrameAnimationEndTime = Date.now() + GREEN_FRAME_ANIMATION_DURATION;
}
});
$('body').on('mouseup', function () {
if (dragMode === DRAGMODE_SELECT) {
// 黄色い枠の大きさが映像の5%以上の場合は、黄色い枠を有効化
const percemtage = yellowFrame.width() * yellowFrame.height() / (canvas1.width * canvas1.height);
console.log(percemtage);
if (percemtage > 0.05) {
isYellowFrameActive = true;
} else {
yellowFrame.hide();
yellowFrame.removeAttr('style');
}
isYellowFrameActive = true;
dragMode = DRAGMODE_NONE;
} else if (dragMode === DRAGMODE_MOVE) {
dragMode = DRAGMODE_NONE;
}
});
projectorButton.on('click', function () {
let projectorWindow = window.open("", "", "width=640,height=480");
projectorWindow.document.write('プロジェクター画面');
canvas2 = document.createElement('canvas');
canvas2.style.aspectRatio = videoElement.videoWidth / videoElement.videoHeight;
context2 = canvas2.getContext('2d');
canvas2.width = videoElement.videoWidth;
canvas2.height = videoElement.videoHeight;
canvas2.style.maxWidth = '100%';
canvas2.style.maxHeight = '100%';
projectorWindow.document.write('');
projectorWindow.document.body.appendChild(canvas2);
});
yellowFrame.on('dblclick', function () {
console.log('dblclick');
if (isYellowFrameActive) {
isYellowFrameActive = false;
yellowFrame.hide();
yellowFrame.removeAttr('style');
greenFrameAnimationEndTime = Date.now() + GREEN_FRAME_ANIMATION_DURATION;
}
});
function draw() {
if (videoElement.paused || videoElement.ended) return;
// Canvas1に映像をそのまま描画
context1.drawImage(videoElement, 0, 0, videoElement.videoWidth, videoElement.videoHeight, 0, 0, canvas1.width, canvas1.height);
// 緑の枠を黄色い枠に合わせていく処理
// 緑の枠の位置とサイズを黄色の枠に徐々に合わせる
// 黄色い枠と緑の枠の位置が同じになっても、緑の枠は表示し続ける
let progress = 1 - (greenFrameAnimationEndTime - Date.now()) / GREEN_FRAME_ANIMATION_DURATION;
if (progress > 1) {
progress = 1;
}
const targetLeft = isYellowFrameActive ? yellowFrame.position().left : 0;
const targetTop = isYellowFrameActive ? yellowFrame.position().top : 0;
const targetWidth = isYellowFrameActive ? yellowFrame.width() : canvas1.width;
const targetHeight = isYellowFrameActive ? yellowFrame.height() : canvas1.height;
let greenFramePosition = greenFrame.position();
let greenFrameWidth = targetWidth * progress + greenFrame.width() * (1 - progress);
let greenFrameHeight = targetHeight * progress + greenFrame.height() * (1 - progress);
let greenFrameLeft = targetLeft * progress + greenFramePosition.left * (1 - progress);
let greenFrameTop = targetTop * progress + greenFramePosition.top * (1 - progress);
greenFrame.css({ left: greenFrameLeft, top: greenFrameTop, width: greenFrameWidth, height: greenFrameHeight });
if (context2 != null) {
// 緑の枠の内部を描画
const greenFramePosition = greenFrame.position();
// 緑の枠の座標系はcanvas1なので、座標系をvideoElementに変換
const x = greenFramePosition.left / canvas1.width * videoElement.videoWidth;
const y = greenFramePosition.top / canvas1.height * videoElement.videoHeight;
const width = greenFrame.width() / canvas1.width * videoElement.videoWidth;
const height = greenFrame.height() / canvas1.height * videoElement.videoHeight;
context2.drawImage(videoElement, x, y, width, height, 0, 0, canvas2.width, canvas2.height);
//context2.drawImage(videoElement, greenFrame.position().left, greenFrame.position().top, greenFrame.position().left + greenFrame.width(), greenFrame.position().top + greenFrame.height(), 0, 0, canvas2.width, canvas2.height);
}
requestAnimationFrame(draw);
}
function updateFramePosition(frame, startX, startY, endX, endY) {
let dx = endX - startX;
let dy = endY - startY;
if (Math.abs(dx) > Math.abs(dy)) {
frame.css({ width: Math.abs(dx), height: Math.abs(dx) / (videoElement.videoWidth / videoElement.videoHeight) });
} else {
frame.css({ height: Math.abs(dy), width: Math.abs(dy) * (videoElement.videoWidth / videoElement.videoHeight) });
}
frame.css({ left: Math.min(startX, endX), top: Math.min(startY, endY) });
}
// 緑の枠の更新作業を追加します
});