JavaScript+html5 canvas实现图片破碎重组动画特效


Posted in Javascript onFebruary 22, 2016

也许你见过HTML5图片破碎动画特效,实现的原理也挺简单的。但是你应该没有见过视频也可以破碎重组,这个HTML5动画就是利用Canvas的相关特性,实现了点击鼠标让视频破碎重组的效果。在视频区域点击鼠标,即可让该区域的视频破碎,让后经过一段时间后,破碎的区域又可以重组还原,视觉效果非常棒。

JavaScript+html5 canvas实现图片破碎重组动画特效

HTML代码

<div style="display:none">
 <video id="sourcevid" autoplay="true" loop="true">
  <source src="BigBuckBunny_640x360.mp4" type="video/mp4"/>
  <source src="BigBuckBunny_640x360.ogv" type="video/ogg"/>
 </video>
 <canvas id="sourcecopy" width="640" height="360"></canvas>
</div>
<div>
<center>
 <div style="z-index:1;position:relative;text-align:center;font-size:16px;font-weight:bold;width:1000px;top:60px;">Click video to blow it up!</div>
 <canvas id="output" width="1000" height="600" onmousedown="dropBomb(event, this)" style="border: 0 none">    </canvas>
</center>
</div>

JavaScript代码

var video;
var copy;
var copycanvas;
var draw;

var TILE_WIDTH = 32;
var TILE_HEIGHT = 24;
var TILE_CENTER_WIDTH = 16;
var TILE_CENTER_HEIGHT = 12;
var SOURCERECT = {x:0, y:0, width:0, height:0};
var PAINTRECT = {x:0, y:0, width:1000, height:600};

function init(){
 video = document.getElementById('sourcevid');
 copycanvas = document.getElementById('sourcecopy');
 copy = copycanvas.getContext('2d');
 var outputcanvas = document.getElementById('output');
 draw = outputcanvas.getContext('2d');
 setInterval("processFrame()", 33);
}
function createTiles(){
 var offsetX = TILE_CENTER_WIDTH+(PAINTRECT.width-SOURCERECT.width)/2;
 var offsetY = TILE_CENTER_HEIGHT+(PAINTRECT.height-SOURCERECT.height)/2;
 var y=0;
 while(y < SOURCERECT.height){
 var x=0;
 while(x < SOURCERECT.width){
  var tile = new Tile();
  tile.videoX = x;
  tile.videoY = y;
  tile.originX = offsetX+x;
  tile.originY = offsetY+y;
  tile.currentX = tile.originX;
  tile.currentY = tile.originY;
  tiles.push(tile);
  x+=TILE_WIDTH;
 }
 y+=TILE_HEIGHT;
 }
}

var RAD = Math.PI/180;
var randomJump = false;
var tiles = [];
var debug = false;
function processFrame(){
 if(!isNaN(video.duration)){
 if(SOURCERECT.width == 0){
  SOURCERECT = {x:0,y:0,width:video.videoWidth,height:video.videoHeight};
  createTiles();
 }
 //this is to keep my sanity while developing
 if(randomJump){
  randomJump = false;
  video.currentTime = Math.random()*video.duration;
 }
 //loop
 if(video.currentTime == video.duration){
  video.currentTime = 0;
 }
 }
 var debugStr = "";
 //copy tiles
 copy.drawImage(video, 0, 0);
 draw.clearRect(PAINTRECT.x, PAINTRECT.y,PAINTRECT.width,PAINTRECT.height);

 for(var i=0; i<tiles.length; i++){
 var tile = tiles[i];
 if(tile.force > 0.0001){
  //expand
  tile.moveX *= tile.force;
  tile.moveY *= tile.force;
  tile.moveRotation *= tile.force;
  tile.currentX += tile.moveX;
  tile.currentY += tile.moveY;
  tile.rotation += tile.moveRotation;
  tile.rotation %= 360;
  tile.force *= 0.9;
  if(tile.currentX <= 0 || tile.currentX >= PAINTRECT.width){
  tile.moveX *= -1;
  }
  if(tile.currentY <= 0 || tile.currentY >= PAINTRECT.height){
  tile.moveY *= -1;
  }
 }else if(tile.rotation != 0 || tile.currentX != tile.originX || tile.currentY != tile.originY){
  //contract
  var diffx = (tile.originX-tile.currentX)*0.2;
  var diffy = (tile.originY-tile.currentY)*0.2;
  var diffRot = (0-tile.rotation)*0.2;

  if(Math.abs(diffx) < 0.5){
  tile.currentX = tile.originX;
  }else{
  tile.currentX += diffx;
  }
  if(Math.abs(diffy) < 0.5){
  tile.currentY = tile.originY;
  }else{
  tile.currentY += diffy;
  }
  if(Math.abs(diffRot) < 0.5){
  tile.rotation = 0;
  }else{
  tile.rotation += diffRot;
  }
 }else{
  tile.force = 0;
 }
 draw.save();
 draw.translate(tile.currentX, tile.currentY);
 draw.rotate(tile.rotation*RAD);
 draw.drawImage(copycanvas, tile.videoX, tile.videoY, TILE_WIDTH, TILE_HEIGHT, -TILE_CENTER_WIDTH, -TILE_CENTER_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
 draw.restore();
 }
 if(debug){
 debug = false;
 document.getElementById('trace').innerHTML = debugStr;
 }
}

function explode(x, y){
 for(var i=0; i<tiles.length; i++){
 var tile = tiles[i];

 var xdiff = tile.currentX-x;
 var ydiff = tile.currentY-y;
 var dist = Math.sqrt(xdiff*xdiff + ydiff*ydiff);

 var randRange = 220+(Math.random()*30);
 var range = randRange-dist;
 var force = 3*(range/randRange);
 if(force > tile.force){
  tile.force = force;
  var radians = Math.atan2(ydiff, xdiff);
  tile.moveX = Math.cos(radians);
  tile.moveY = Math.sin(radians);
  tile.moveRotation = 0.5-Math.random();
 }
 }
 tiles.sort(zindexSort);
 processFrame();
}
function zindexSort(a, b){
 return (a.force-b.force);
}

function dropBomb(evt, obj){
 var posx = 0;
 var posy = 0;
 var e = evt || window.event;
 if (e.pageX || e.pageY){
 posx = e.pageX;
 posy = e.pageY;
 }else if (e.clientX || e.clientY) {
 posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
 posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
 }
 var canvasX = posx-obj.offsetLeft;
 var canvasY = posy-obj.offsetTop;
 explode(canvasX, canvasY);
}

function Tile(){
 this.originX = 0;
 this.originY = 0;
 this.currentX = 0;
 this.currentY = 0;
 this.rotation = 0;
 this.force = 0;
 this.z = 0;
 this.moveX= 0;
 this.moveY= 0;
 this.moveRotation = 0;

 this.videoX = 0;
 this.videoY = 0;
}

/*
 getPixel
 return pixel object {r,g,b,a}
*/
function getPixel(imageData, x, y){
 var data = imageData.data;
 var pos = (x + y * imageData.width) * 4;
 return {r:data[pos], g:data[pos+1], b:data[pos+2], a:data[pos+3]}
}
/*
 setPixel
 set pixel object {r,g,b,a}
*/
function setPixel(imageData, x, y, pixel){
 var data = imageData.data;
 var pos = (x + y * imageData.width) * 4;
 data[pos] = pixel.r;
 data[pos+1] = pixel.g;
 data[pos+2] = pixel.b;
 data[pos+3] = pixel.a;
}
/*
 copyPixel
 faster then using getPixel/setPixel combo
*/
function copyPixel(sImageData, sx, sy, dImageData, dx, dy){
 var spos = (sx + sy * sImageData.width) * 4;
 var dpos = (dx + dy * dImageData.width) * 4;
 dImageData.data[dpos] = sImageData.data[spos];   //R
 dImageData.data[dpos+1] = sImageData.data[spos+1]; //G
 dImageData.data[dpos+2] = sImageData.data[spos+2]; //B
 dImageData.data[dpos+3] = sImageData.data[spos+3]; //A
}
</script>

希望本文所述对大家学习javascript程序设计有所帮助。

Javascript 相关文章推荐
javascript中实现兼容JAVA的hashCode算法代码分享
Aug 11 Javascript
JavaScript的Number对象的toString()方法
Dec 18 Javascript
7个去伪存真的JavaScript面试题
Jan 07 Javascript
Zero Clipboard实现浏览器复制到剪贴板的方法(多个复制按钮)
Mar 24 Javascript
jquery+ajax+text文本框实现智能提示完整实例
Jul 09 Javascript
完全深入学习Bootstrap表单
Nov 28 Javascript
详解如何在React组件“外”使用父组件的Props
Jan 12 Javascript
解决element UI 自定义传参的问题
Aug 22 Javascript
详解Vue前端对axios的封装和使用
Apr 01 Javascript
JavaScript页面倒计时功能完整示例
May 15 Javascript
详解用async/await来处理异步
Aug 28 Javascript
Vue实现穿梭框效果
Sep 30 Javascript
jQuery获取字符串中出现最多的数
Feb 22 #Javascript
jQuery基于muipicker实现仿ios时间选择
Feb 22 #Javascript
简单谈谈javascript中this的隐式绑定
Feb 22 #Javascript
javascript实现一个简单的弹出窗
Feb 22 #Javascript
Js的Array数组对象详解
Feb 22 #Javascript
AngularJS中使用HTML5手机摄像头拍照
Feb 22 #Javascript
JS字符串的切分用法实例
Feb 22 #Javascript
You might like
无法载入 mcrypt 扩展,请检查 PHP 配置终极解决方案
2011/07/18 PHP
PHP书写格式详解(必看)
2016/05/23 PHP
php文件上传类的分享
2017/07/06 PHP
搜索附近的人PHP实现代码
2018/02/11 PHP
向当前style sheet中插入一个新的style实现方法
2013/04/01 Javascript
通过隐藏iframe实现文件下载的js方法介绍
2014/02/26 Javascript
jQuery获取页面元素绝对与相对位置的方法
2015/06/10 Javascript
基于jQuery日历插件制作日历
2016/03/11 Javascript
nodejs简单实现操作arduino
2016/09/25 NodeJs
PHP实现记录代码运行时间封装类实例教程
2017/05/08 Javascript
Angular移动端页面input无法输入的解决方法
2017/11/14 Javascript
JavaScript实现仿Clock ISO时钟
2018/06/29 Javascript
解决vue无法设置滚动位置的问题
2018/10/07 Javascript
详解vue数组遍历方法forEach和map的原理解析和实际应用
2018/11/15 Javascript
JavaScript ECMA-262-3 深入解析(二):变量对象实例详解
2020/04/25 Javascript
微信小程序接入vant Weapp组件的详细步骤
2020/10/28 Javascript
[01:12:53]完美世界DOTA2联赛PWL S2 Forest vs SZ 第一场 11.25
2020/11/26 DOTA
Python列表计数及插入实例
2014/12/17 Python
python导出hive数据表的schema实例代码
2018/01/22 Python
python执行精确的小数计算方法
2019/01/21 Python
python爬虫爬取微博评论案例详解
2019/03/27 Python
keras CNN卷积核可视化,热度图教程
2020/06/22 Python
Python使用tkinter实现摇骰子小游戏功能的代码
2020/07/02 Python
python MD5加密的示例
2020/10/19 Python
20行代码教你用python给证件照换底色的方法示例
2021/02/05 Python
css3 transform导致子元素固定定位变成绝对定位的方法
2020/03/06 HTML / CSS
医学检验专业大学生求职信
2013/11/18 职场文书
农民致富事迹材料
2014/01/23 职场文书
2014学校领导四风问题对照检查材料思想汇报
2014/09/22 职场文书
整改落实情况汇报材料
2014/10/29 职场文书
婚前保证书范文
2015/02/28 职场文书
小学毕业感言100字
2015/07/30 职场文书
党员干部学习十八届五中全会精神心得体会
2016/01/05 职场文书
Go 语言下基于Redis分布式锁的实现方式
2021/06/28 Golang
Nginx stream 配置代理(Nginx TCP/UDP 负载均衡)
2021/11/17 Servers
muduo TcpServer模块源码分析
2022/04/26 Redis