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 相关文章推荐
学习ExtJS border布局
Oct 08 Javascript
JS OOP包机制,类创建的方法定义
Nov 02 Javascript
纯JAVASCRIPT图表动画插件Highcharts Examples
Apr 16 Javascript
jQuery实现DIV层收缩展开的方法
Feb 27 Javascript
javascript实现链接单选效果的方法
May 13 Javascript
JavaScript中Boolean对象的属性解析
Oct 21 Javascript
JavaScript隐式类型转换
Mar 15 Javascript
浅析如何利用angular结合translate为项目实现国际化
Dec 08 Javascript
[js高手之路]图解javascript的原型(prototype)对象,原型链实例
Aug 28 Javascript
vue框架搭建之axios使用教程
Jul 11 Javascript
使用ECharts实现状态区间图
Oct 25 Javascript
小程序实现新用户判断并跳转激活的方法
May 20 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
php csv操作类代码
2009/12/14 PHP
浅析PHP中的UNICODE 编码与解码
2013/06/29 PHP
php时间戳格式化显示友好的时间函数分享
2014/10/21 PHP
PHP入门教程之日期与时间操作技巧总结(格式化,验证,获取,转换,计算等)
2016/09/11 PHP
php支付宝系列之电脑网站支付
2018/05/30 PHP
javascript eval和JSON之间的联系
2009/12/31 Javascript
JavaScript实现快速排序(自已编写)
2012/12/19 Javascript
jQuery动态设置form表单的enctype值(实现代码)
2013/07/04 Javascript
js实现的点击数量加一可操作数据库
2014/05/09 Javascript
Node.js中防止错误导致的进程阻塞的方法
2016/08/11 Javascript
浅谈移动端之js touch事件 手势滑动事件
2016/11/07 Javascript
利用JS实现页面删除并重新排序功能
2016/12/09 Javascript
如何实现星星评价(jquery.raty.js插件)
2016/12/21 Javascript
js通过Date对象实现倒计时动画效果
2017/10/27 Javascript
Vue的实例、生命周期与Vue脚手架(vue-cli)实例详解
2017/12/27 Javascript
vue.js 中使用(...)运算符报错的解决方法
2018/08/09 Javascript
如何检查一个对象是否为空
2019/04/11 Javascript
vue项目配置使用flow类型检查的步骤
2020/03/18 Javascript
Element-ui树形控件el-tree自定义增删改和局部刷新及懒加载操作
2020/08/31 Javascript
Node.js中的异步生成器与异步迭代详解
2021/01/31 Javascript
[01:38:19]夜魇凡尔赛茶话会 第五期
2021/03/11 DOTA
跟老齐学Python之用while来循环
2014/10/02 Python
python 生成器生成杨辉三角的方法(必看)
2017/04/10 Python
Python用户推荐系统曼哈顿算法实现完整代码
2017/12/01 Python
查看Django和flask版本的方法
2018/05/14 Python
python pandas库中DataFrame对行和列的操作实例讲解
2018/06/09 Python
Python3.7.0 Shell添加清屏快捷键的实现示例
2020/03/23 Python
python轮询机制控制led实例
2020/05/03 Python
Django静态文件加载失败解决方案
2020/08/26 Python
稀有和绝版书籍:Biblio.com
2017/02/02 全球购物
GafasWorld西班牙:购买太阳镜、眼镜和隐形眼镜
2019/09/08 全球购物
大二法英学生职业生涯规划范文
2014/02/27 职场文书
初中学校对照检查材料
2014/08/19 职场文书
毕业论文致谢信
2015/05/14 职场文书
退伍军人感言
2015/08/01 职场文书
2016毕业实习单位评语大全
2015/12/01 职场文书