progress: video 객체가 비디오 로딩 과정에 관한 정보를 갱신할 때 발생. 로딩율 정보 갱신.
(※브라우저별로 로딩이 끝났는지 판단하는 부분이 잘 지원이 안되는듯하므로 확인 필요)
canplaythrough: 비디오 전체를 재생할 만큼 충분히 비디오가 로드되면 발생.
재생 시작시점을 알 수 있다.
▼ HTML5 비디오 프리로딩 소스 [2012.01 - 브라우저들에서 미동작]
<!doctype html>
<html lang="ko">
<head>
<title>CH6EX5: Basic HTML5 Preleoading Video
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded() {
var videoElement = document.getElementById("thevideo");
videoElement.addEventListener('progress',updateLoadingStatus,false);
videoElement.addEventListener('canplaythrough',playVideo,false);
//playVideo(); // 구글 크롬 작동을 위해서는 추가해야함 [2012.01]
}
function updateLoadingStatus() {
var loadingStatus = document.getElementById("loadingStatus");
var videoElement = document.getElementById("thevideo");
var percentLoaded = parseInt(((videoElement.buffered.end(0) /videoElement.duration) * 100));
document.getElementById("loadingStatus").innerHTML = percentLoaded + '%';
}
}
function playVideo() {
var videoElement = document.getElementById("thevideo");
videoElement.play();
}
0%
</body>
</html>
6.5 비디오와 캔버스
- 비디오는 반드시 HTML에 포함되어야 한다.
- <div style="display: none"> 안에 포함시킬 것 (로딩하는 동안 재생 방지)
6.6 캔버스 상의 비디오 예제 애플리케이션
▼ [예제 6-8] 비디오 이벤트 (재생경과 시간에 따라 메시지 출력) +
[예제 6-9] 비디오 회전
<!doctype html>
<html lang="ko">
<head>
<title>CH6EX8: 간단한 비디오 이벤트 + CH6EX9: 회전
<script src="modernizr-1.6.min.js">
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
var videoElement; // 비디오 요소
var videoDiv; // 비디오를 포함 시킬 DIV 요소
function eventWindowLoaded() {
videoElement = document.createElement("video");
videoDiv = document.createElement('div');
document.body.appendChild(videoDiv);
videoDiv.appendChild(videoElement);
videoDiv.setAttribute("style", "display:none;");
var videoType = supportedVideoFormat(videoElement);
if (videoType == "") {
alert("no video support");
return;
}
videoElement.setAttribute("src", "muirbeach." + videoType);
videoElement.addEventListener("canplaythrough",videoLoaded,false);
}
function supportedVideoFormat(video) {
var returnExtension = "";
// 브라우저가 지원하는 비디오 타입 체크("":미지원, maybe:확실하지 않음, probably:지원)
if (video.canPlayType("video/webm") =="probably" || video.canPlayType("video/webm") == "maybe") {
returnExtension = "webm";
} else if(video.canPlayType("video/mp4") == "probably" || video.canPlayType("video/mp4") == "maybe") {
returnExtension = "mp4";
} else if(video.canPlayType("video/ogg") =="probably" || video.canPlayType("video/ogg") == "maybe") {
returnExtension = "ogg";
}
return returnExtension;
}
function canvasSupport () {
return Modernizr.canvas;
}
function videoLoaded() {
canvasApp();
}
function canvasApp() {
if (!canvasSupport()) { return; }
//*** set roationvalue
var rotation = 0;
//***
function drawScreen () {
//Background
context.fillStyle = '#ffffaa';
context.fillRect(0, 0, theCanvas.width, theCanvas.height);
//Box
context.strokeStyle = '#000000';
context.strokeRect(5, 5, theCanvas.width-10, theCanvas.height-10);
//video
// context.drawImage(videoElement , 85, 30);
//video
//*** Start rotation calculation
context.save();
context.setTransform(1,0,0,1,0,0);
var angleInRadians =rotation * Math.PI / 180;
var x=85;
var y=30;
var videoWidth=320;
var videoHeight=240;
context.translate(x+.5*videoWidth, y+.5*videoHeight); // move the origin of the transform to the center of the video window;
context.rotate(angleInRadians);
//****
context.drawImage(videoElement ,-.5*videoWidth, -.5*videoHeight);
//Display Message
for (var i =0; i < messages.length ; i++) {
var tempMessage = messages[i];
if (videoElement.currentTime > tempMessage.time) {
context.font = "bold 14px sans";
context.fillStyle = "#FFFF00";
context.fillText (tempMessage.message,
tempMessage.x-.5*videoWidth,
tempMessage.y-.5*videoHeight);
}
}
//*** restore screen
context.restore();
rotation++;
//***
// Text
context.fillStyle = "#000000";
context.font = "10px sans";
context.fillText ("Duration:" + videoElement.duration, 10 , 20);
context.fillText ("Current time:" + videoElement.currentTime, 260 ,280);
context.fillText ("Loop: " + videoElement.loop, 10 ,290);
context.fillText ("Autoplay: " + videoElement.autoplay, 80 ,290);
context.fillText ("Muted: " + videoElement.muted, 160 ,290);
context.fillText ("Controls: " + videoElement.controls, 240 ,290);
context.fillText ("Volume: " + videoElement.volume, 320 ,290);
} // End of drawScreen()
var messages = new Array();
messages[0] = {time:0,message:"", x:0 ,y:0};
messages[1] = {time:1,message:"This Is Muir Beach!", x:90 ,y:200};
messages[2] = {time:4,message:"Look At Those Waves!", x:240 ,y:240};
messages[3] = {time:8,message:"Look At Those Rocks!", x:100 ,y:100};
var theCanvas = document.getElementById('canvasOne');
var context = theCanvas.getContext('2d');
videoElement.play();
// 30프레임(frame per second)마다 drawScreen() 실행으로 화면 갱신
setInterval(drawScreen, 33);
}
</script>
</head>
<body>
<canvas id="canvasOne" width="500" height="300">
Your browser does not support the HTML 5 Canvas.
</canvas>
</body>
</html>
▼ 실행결과
▼ [예제 6-10] 비디오 퍼즐 - 순서대로 만들어보자
① 캔버스 상에 비디오를 로드하되 아직 출력하지는 않는다.
② 몇 조각으로 퍼즐을 만들지 결정
③ 퍼즐조각을 감을 board 배열 생성
④ 퍼즐판은 4x4 격자 모양
⑤ 퍼즐판 위에 퍼즐 조각을 무작위로 섞는다.
⑥ 마우스 버튼을 위한 이벤트 리스너 추가(mouseup)
⑦ 일정 시간마다 drawScreen() 호출: 33fps
⑧ 사용자가 퍼즐 조각을 누르기를 대기
⑨ 대기하는 동안 비디오 조각들은 재생중
⑩ 사용자가 퍼즐 조각을 누르면 노란색으로 표시
⑪ 사용자가 2개 조각을 선택하면 서로 위치 바꾸기
⑫ 사용자가 퍼즐을 다 맞추면 비디오의 원래 모습 보이기
<!doctype html>
<html lang="ko">
<head>
<title>CH6EX10 : Video Puzzle
<script src="modernizr-1.6.min.js">
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
var videoElement;
var videoDiv;
function eventWindowLoaded() {
videoElement = document.createElement("video");
videoDiv = document.createElement('div');
document.body.appendChild(videoDiv);
videoDiv.appendChild(videoElement);
videoDiv.setAttribute("style", "display:none;");
var videoType = supportedVideoFormat(videoElement);
if (videoType == "") {
alert("no video support");
return;
}
videoElement.setAttribute("muted","true");
videoElement.setAttribute("src", "muirbeach." + videoType);
videoElement.setAttribute("loop", "true");
videoElement.addEventListener("canplaythrough",videoLoaded,false);
}
function supportedVideoFormat(video) {
var returnExtension = "";
if (video.canPlayType("video/webm") =="probably" || video.canPlayType("video/webm") == "maybe") {
returnExtension = "webm";
} else if(video.canPlayType("video/mp4") == "probably" || video.canPlayType("video/mp4") == "maybe") {
returnExtension = "mp4";
} else if(video.canPlayType("video/ogg") =="probably" || video.canPlayType("video/ogg") == "maybe") {
returnExtension = "ogg";
}
return returnExtension;
}
function canvasSupport () {
return Modernizr.canvas;
}
function videoLoaded() {
canvasApp();
}
function canvasApp() {
if (!canvasSupport()) {
return;
}
function drawScreen () {
//Background
context.fillStyle = '#303030';
context.fillRect(0, 0, theCanvas.width, theCanvas.height);
//Box
context.strokeStyle = '#FFFFFF';
context.strokeRect(5, 5, theCanvas.width-10, theCanvas.height-10);
for (var c = 0; c < cols; c++) {
for (var r = 0; r < rows; r++) {
var tempPiece = board[c][r];
var imageX = tempPiece.finalCol*partWidth;
var imageY = tempPiece.finalRow*partHeight;
var placeX = c*partWidth+c*xPad+startXOffset;
var placeY = r*partHeight+r*yPad+startYOffset;
//context.drawImage(videoElement , imageX, imageY, partWidth, partHeight);
context.drawImage(videoElement, imageX, imageY, partWidth, partHeight, placeX, placeY, partWidth, partHeight);
if (tempPiece.selected) {
context.strokeStyle = '#FFFF00';
context.strokeRect( placeX, placeY, partWidth, partHeight);
}
}
}
}
function randomizeBoard(board) {
var newBoard = new Array();
var cols = board.length;
var rows = board[0].length
for (var i = 0; i < cols; i++) {
newBoard[i] = new Array();
for (var j =0; j < rows; j++) {
var found = false;
var rndCol = 0;
var rndRow = 0;
while (!found) {
var rndCol = Math.floor(Math.random() * cols);
var rndRow = Math.floor(Math.random() * rows);
if (board[rndCol][rndRow] != false) {
found = true;
}
}
newBoard[i][j] = board[rndCol][rndRow];
board[rndCol][rndRow] = false;
}
}
return newBoard;
}
function eventMouseUp(event) {
var mouseX;
var mouseY;
var pieceX;
var pieceY;
if ( event.layerX || event.layerX == 0) { // Firefox
mouseX = event.layerX ;
mouseY = event.layerY;
} else if (event.offsetX || event.offsetX == 0) { // Opera
mouseX = event.offsetX;
mouseY = event.offsetY;
}
var selectedList= new Array();
for (var c = 0; c < cols; c++) {
for (var r =0; r < rows; r++) {
pieceX = c*partWidth+c*xPad+startXOffset;
pieceY = r*partHeight+r*yPad+startYOffset;
if ( (mouseY >= pieceY) && (mouseY <= pieceY+partHeight) && (mouseX >= pieceX) && (mouseX <= pieceX+partWidth) ) {
if ( board[c][r].selected) {
board[c][r].selected = false;
} else {
board[c][r].selected = true;
}
}
if (board[c][r].selected) {
selectedList.push({col:c,row:r})
}
}
}
if (selectedList.length == 2) {
var selected1 = selectedList[0];
var selected2 = selectedList[1];
var tempPiece1 = board[selected1.col][selected1.row];
board[selected1.col][selected1.row] = board[selected2.col][selected2.row];
board[selected2.col][selected2.row] = tempPiece1;
board[selected1.col][selected1.row].selected = false;
board[selected2.col][selected2.row].selected = false;
}
}
var theCanvas = document.getElementById('canvasOne');
var context = theCanvas.getContext('2d');
videoElement.loop = true;
videoElement.play();
//Puzzle Settings
var rows = 4;
var cols = 4;
var xPad = 10;
var yPad = 10;
var startXOffset = 10;
var startYOffset = 10;
var partWidth = videoElement.width/cols;
var partHeight = videoElement.height/rows;
//320x240
partWidth=80;
partHeight=60;
var board = new Array();
//Initialize Board
for (var i = 0; i < cols; i++) {
board[i] = new Array();
for (var j =0; j < rows; j++) {
board[i][j] = { finalCol:i,finalRow:j,selected:false };
}
}
board = randomizeBoard(board);
theCanvas.addEventListener("mouseup",eventMouseUp, false);
setInterval(drawScreen, 33);
}
</script>
</head>
<body>