昨天咱們剖析了一下翻閱體驗的實現。今天要剖析另一個頗有意思的效果——視頻拼圖。css
網站中第一部分第二頁《月熊的標誌》是月熊志中互動性較強的一頁,頁面上會隨機分佈9塊視頻碎片,用戶能夠經過鼠標或者觸控移動碎片完成拼圖。css3
在這個Demo中,咱們須要引用2個JavaScript庫,jQuery和Hammer.js。canvas
Hammer.js 是一個手勢觸控JS庫,可以爲網頁加入Tap、Swipe、Drag等事件,而且同時支持鼠標和觸控輸入,免去本身監聽事件和判斷瀏覽器兼容等問題。數組
首先,咱們在頁面中創建一個九宮格:瀏覽器
1 HTML: 2 <div id="puzzle"> 3 <div class="container"><i></i></div> 4 … 5 <div class="container"><i></i></div> 6 </div> 7 8 CSS: 9 #puzzle { 10 position: absolute; 11 top: 50%; 12 left: 38%; 13 margin-top: -190px; 14 width: 678px; 15 height: 381px; 16 } 17 18 #puzzle .container { 19 float: left; 20 width: 226px; 21 height: 127px; 22 } 23 24 #puzzle .container i { 25 display: block; 26 margin: 4px; 27 width: 218px; 28 height: 119px; 29 background: #fff; 30 }
每一個宮格(.container)的大小是226*127,其中白色部分(.container i)是218*119。app
接着咱們在頁面中插入視頻,咱們使用HTML5 中新增的Video標籤,而且爲了兼容多數瀏覽器,使用了2種格式的視頻源,而後設置視頻爲自動播放(Autoplay)和循環播放(Loop),視頻源的大小建議和九宮格保持一致:dom
1 HTML: 2 <video width="678" height="381" id="video" autoplay loop> 3 <source src="../video/findjasper.mp4" type="video/mp4"> 4 <source src="../video/findjasper.ogv" type="video/ogg; codecs='theora, vorbis'"> 5 </video>
最後把視頻隱藏起來,在幕後默默的運行便可:ide
1 CSS: 2 #video { 3 display: none; 4 }
視頻碎片自己是一個個canvas元素,經過JS將Video的幀畫面分塊循環繪製到canvas上。函數
1 JS: 2 //爲數組添加隨機打亂方法 3 Array.prototype.shuffle = function () { 4 var l = this.length, 5 i = l; 6 while (i--) { 7 var p = parseInt(Math.random() * l), 8 t = this[i]; 9 this[i] = this[p]; 10 this[p] = t; 11 }; 12 return this; 13 };
1 //隨機函數,隨機返回min~max中的任一數值 2 function random(min, max) { 3 return parseInt(Math.random() * (max - min + 1) + min); 4 };
上面的JS方法/函數接下來會用到。oop
1 S: 2 var PIECE_WIDTH = 226, 3 PIECE_HEIGHT = 127, 4 $body = $("body"), 5 video = $("#video")[0], 6 $puzzle = $("#puzzle"), 7 $puzzleItems = $puzzle.find(".container"), 8 zIndex = 2, 9 ctxs = [], 10 rndArray = [0, 1, 2, 3, 4, 5, 6, 7, 8].shuffle();
以上是會用到的變量,其中須要特別說明的是zIndex用來保存碎片的z-index值,ctxs用來保存碎片的canvas上下文,rndArray是一個0~8的隨機數組。
1 JS: 2 //循環建立碎片 3 for (var i = 0; i < 9; i++) { 4 var index = rndArray[i], //分配隨機位置 5 piece = document.createElement("canvas"); //建立canvas元素 6 7 piece.className = "piece"; 8 piece.width = PIECE_WIDTH; 9 piece.height = PIECE_HEIGHT; 10 ctxs.push(piece.getContext("2d")); //把上下文push到ctxs數組,方便繪製時調用 11 12 //使用random函數給碎片設置一個隨機的位置 13 //使用css3 transform旋轉碎片角度,使拼圖更加真實 14 //最後把碎片所對應的宮格(.container)保存到data("container")中 15 $(piece).css({ 16 left: random(50, window.innerWidth - PIECE_WIDTH), 17 top: random(50, window.innerHeight - PIECE_HEIGHT), 18 transform: "rotate(" + random(-25, 25) + "deg)" 19 }).data("container", $puzzleItems.eq(index)).appendTo($body); 20 };
目前並無限制碎片出現的位置,因此碎片可能會遮蓋頁面上的文字,你能夠本身加以完善。
若是如今運行頁面,你就能看到頁面上出現的隨機碎片了,可是因爲尚未把視頻繪製到頁面上,因此只能看到黑色。
繪製視頻到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表示繪製到畫布上的寬和高。
看文字的話可能比較難懂,看下面這張圖應該能幫助你理解:
咱們建立一個drawVideo函數繪製視頻
1 JS: 2 function drawVideo() { 3 for (var i = 0; i < 9; i++) { 4 var index = rndArray[i], //當前碎片位置 5 row = Math.floor(index / 3), //由於宮格是3x3的,因此用取餘數獲取行數 6 col = Math.floor(index % 3); //與3取模得到列數 7 ctxs[i].drawImage(video, (col * PIECE_WIDTH), (row * PIECE_HEIGHT), PIECE_WIDTH, PIECE_HEIGHT, 0, 0, PIECE_WIDTH, PIECE_HEIGHT); //row和col分別乘以宮格的寬高就是要從視頻上繪製的左上角座標 8 }; 9 }; 10 setInterval(drawVideo, 50);
而後調用setInterval每隔50毫秒循環繪製。
hammer.js的事件對象添加一個gesture對象,裏面保存了關於這次操做的相關信息,好比移動距離、移動速率、移動角度、持續時間等。
咱們先建立3個函數,對應拖拽過程當中的3個事件。
開始拖拽碎片時,加上.dragging(添加box-shadow),並設置更高一層的z-index保證在全部碎片之上,最後保存當前的位置到data("offset")中。
1 function dragStart(){ 2 var $piece = $(this); 3 $piece.addClass("dragging").css("z-index", zIndex++).data("offset", $piece.offset()); 4 };
在拖拽碎片時,只須要給以前保存的offset加上移動距離(deltaX/ deltaY),就是如今的正確位置。
1 function drag(event){ 2 var $piece = $(this), 3 pieceOffset = $piece.data("offset"); 4 $piece.css({ 5 left: pieceOffset.left + event.gesture.deltaX, 6 top: pieceOffset.top + event.gesture.deltaY 7 }); 8 };
最後拖拽結束,移除dragging類。
若是碎片的中心點在對應的宮格內部,就移動的宮格內,並關閉hammer。
若是九宮格內有9塊碎片,就完成拼圖了!
1 function dragEnd(event){ 2 var $piece = $(this), 3 pieceOffset = $piece.data("offset"); 4 5 $piece.removeClass("dragging"); 6 7 var centerX = pieceOffset.left + event.gesture.deltaX + PIECE_WIDTH / 2, 8 centerY = pieceOffset.top + event.gesture.deltaY + PIECE_HEIGHT / 2, 9 $container = $piece.data("container"), 10 containerOffset = $container.offset(); 11 12 13 if (centerX > containerOffset.left && (centerX < containerOffset.left + PIECE_WIDTH) && centerY > containerOffset.top && centerY < (containerOffset.top + PIECE_HEIGHT)) { 14 $container.prepend($piece.removeAttr("style").data("hammer").off()); 15 if ($puzzle.find(".piece").length == 9) { 16 // bingo. 17 }; 18 }; 19 }; 20 21 22 $(".piece").hammer({ 23 prevent_default: true 24 }).on("dragstart", dragStart).on("drag", drag).on("dragend", dragEnd);
最後初始化碎片並綁定函數到對應的事件上,這個互動小遊戲就完成了。
其實,網站上還有不少特效的製做,你們能夠本身前去體驗:http://moonbear.animalsasia.org/ie/。但願本文能給你們帶來一些靈感,運用到本身的網站開發上面去。