pcBeta 发表于 2013-12-25 16:52

《月熊志》技术解析——创新的视频拼图

网站中第一部分第二页《月熊的标志》是月熊志中互动性较强的一页,页面上会随机分布9块视频碎片,用户可以通过鼠标或者触控移动碎片完成拼图。                                http://static.cdn.pcbeta.com/data/attachment/album/201312/25/164906p5cgtyz6vyqqw1w2.png在这个Demo中,我们需要引用2个JavaScript库,jQuery和Hammer.js。Hammer.js 是一个手势触控JS库,能够为网页加入Tap、Swipe、Drag等事件,并且同时支持鼠标和触控输入,免去自己监听事件和判断浏览器兼容等问题。 建立九宫格首先,我们在页面中建立一个九宫格: HTML:<div id="puzzle">         <divclass="container"><i></i></div>         …         <divclass="container"><i></i></div></div> CSS:#puzzle {   position: absolute;   top: 50%;   left: 38%;   margin-top: -190px;   width: 678px;   height: 381px;} #puzzle .container {   float: left;   width: 226px;   height: 127px;} #puzzle .container i {   display: block;   margin: 4px;   width: 218px;   height: 119px;   background: #fff;} http://static.cdn.pcbeta.com/data/attachment/album/201312/25/164906jgsill5qq5c7qc35.png 每个宫格(.container)的大小是226*127,其中白色部分(.container i)是218*119。 插入视频接着我们在页面中插入视频,我们使用HTML5 中新增的Video标签,并且为了兼容多数浏览器,使用了2种格式的视频源,然后设置视频为自动播放(Autoplay)和循环播放(Loop),视频源的大小建议和九宫格保持一致: HTML:<video width="678" height="381"id="video" autoplay loop>   <source src="../video/findjasper.mp4"type="video/mp4">   <sourcesrc="../video/findjasper.ogv" type="video/ogg; codecs='theora,vorbis'"></video> 最后把视频隐藏起来,在幕后默默的运行即可:CSS:#video {    display: none;}创建视频碎片视频碎片本身是一个个canvas元素,通过JS将Video的帧画面分块循环绘制到canvas上。 JS://为数组添加随机打乱方法Array.prototype.shuffle = function () {   var l = this.length,            i = l;   while (i--) {            var p =parseInt(Math.random() * l),                     t =this;            this =this;            this = t;   };   return this;}; //随机函数,随机返回min~max中的任一数值function random(min, max) {   returnparseInt(Math.random() * (max - min + 1) + min);}; 上面的JS方法/函数接下来会用到。 JS:var PIECE_WIDTH = 226,    PIECE_HEIGHT = 127,    $body =$("body"),    video =$("#video"),$puzzle = $("#puzzle"),$puzzleItems = $puzzle.find(".container"),    zIndex = 2,    ctxs = [],rndArray = .shuffle(); 以上是会用到的变量,其中需要特别说明的是zIndex用来保存碎片的z-index值,ctxs用来保存碎片的canvas上下文,rndArray是一个0~8的随机数组。 JS://循环创建碎片for (var i = 0; i < 9; i++) {    var index = rndArray,//分配随机位置      piece =document.createElement("canvas"); //创建canvas元素     piece.className ="piece";    piece.width = PIECE_WIDTH;    piece.height =PIECE_HEIGHT;   ctxs.push(piece.getContext("2d")); //把上下文push到ctxs数组,方便绘制时调用 //使用random函数给碎片设置一个随机的位置//使用css3 transform旋转碎片角度,使拼图更加真实//最后把碎片所对应的宫格(.container)保存到data("container")中    $(piece).css({      left: random(50,window.innerWidth - PIECE_WIDTH),      top: random(50,window.innerHeight - PIECE_HEIGHT),      transform:"rotate(" + random(-25, 25) + "deg)"   }).data("container", $puzzleItems.eq(index)).appendTo($body);}; 目前并没有限制碎片出现的位置,所以碎片可能会遮盖页面上的文字,你可以自己加以完善。 如果现在运行页面,你就能看到页面上出现的随机碎片了,但是由于还没有把视频绘制到页面上,所以只能看到黑色。 http://static.cdn.pcbeta.com/data/attachment/album/201312/25/164907rj2zrislhiwjjh7e.png绘制视频碎片绘制视频到canvas其实十分简单,主要用到的是canvas的drawImage方法。drawImage方法接收9个参数。 context.drawImage(image, sourceX, sourceY,sourceWidth, sourceHeight, destX, destY, destWidth, destHeight) image指向要使用的来源,可以是图片、视频或者Canvas元素,sourceX和sourceY指image上绘制的左上角坐标,sourceWidth 和 sourceHeight指从image上要绘制的宽和高, destX和destY表示将image绘制到画布上的左上角坐标,destWidth 和destHeight表示绘制到画布上的宽和高。 看文字的话可能比较难懂,看下面这张图应该能帮助你理解: http://static.cdn.pcbeta.com/data/attachment/album/201312/25/164907bfhxxg71e4x5f226.png我们创建一个drawVideo函数绘制视频 JS:function drawVideo() {    for (var i = 0; i < 9;i++) {      var index =rndArray, //当前碎片位置            row =Math.floor(index / 3), //因为宫格是3x3的,所以用取余数获取行数            col =Math.floor(index % 3); //与3取模获得列数       ctxs.drawImage(video, (col * PIECE_WIDTH), (row * PIECE_HEIGHT),PIECE_WIDTH, PIECE_HEIGHT, 0, 0, PIECE_WIDTH, PIECE_HEIGHT); //row和col分别乘以宫格的宽高就是要从视频上绘制的左上角坐标    };};setInterval(drawVideo, 50); 然后调用setInterval每隔50毫秒循环绘制。 http://static.cdn.pcbeta.com/data/attachment/album/201312/25/1649074q5303f1n05qntzt.png 处理拖拽操作hammer.js的事件对象添加一个gesture对象,里面保存了关于此次操作的相关信息,比如移动距离、移动速率、移动角度、持续时间等。 我们先创建3个函数,对应拖拽过程中的3个事件。 开始拖拽碎片时,加上.dragging(添加box-shadow),并设置更高一层的z-index保证在所有碎片之上,最后保存当前的位置到data("offset")中。function dragStart(){   var $piece = $(this);   $piece.addClass("dragging").css("z-index",zIndex++).data("offset", $piece.offset());}; 在拖拽碎片时,只需要给之前保存的offset加上移动距离(deltaX/ deltaY),就是现在的正确位置。function drag(event){   var $piece = $(this),       pieceOffset = $piece.data("offset");   $piece.css({       left: pieceOffset.left + event.gesture.deltaX,       top: pieceOffset.top + event.gesture.deltaY   });}; 最后拖拽结束,移除dragging类。如果碎片的中心点在对应的宫格内部,就移动的宫格内,并关闭hammer。如果九宫格内有9块碎片,就完成拼图了!function dragEnd(event){   var $piece = $(this),       pieceOffset = $piece.data("offset");    $piece.removeClass("dragging");    var centerX = pieceOffset.left + event.gesture.deltaX + PIECE_WIDTH / 2,       centerY = pieceOffset.top + event.gesture.deltaY + PIECE_HEIGHT / 2,       $container = $piece.data("container"),       containerOffset = $container.offset();    if (centerX > containerOffset.left && (centerX <containerOffset.left + PIECE_WIDTH) && centerY > containerOffset.top&& centerY < (containerOffset.top + PIECE_HEIGHT)) {       $container.prepend($piece.removeAttr("style").data("hammer").off());       if ($puzzle.find(".piece").length == 9) {         // bingo.       };   };}; $(".piece").hammer({   prevent_default: true}).on("dragstart", dragStart).on("drag",drag).on("dragend", dragEnd); 最后初始化碎片并绑定函数到对应的事件上,这个互动小游戏就完成了。 http://static.cdn.pcbeta.com/data/attachment/album/201312/25/164908xi31zq5fusa63f55.png
页: [1]
查看完整版本: 《月熊志》技术解析——创新的视频拼图