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学习笔记(九)javascript中的原型(prototype)及原型链的继承方式
Apr 12 Javascript
javascript中xml操作实现代码
Nov 21 Javascript
jquery乱码与contentType属性设置问题解决方案
Jan 07 Javascript
jquery五角星评分插件示例分享
Feb 21 Javascript
JQuery radio(单选按钮)操作方法汇总
Apr 15 Javascript
JavaScript中的toDateString()方法使用详解
Jun 12 Javascript
Ajax使用原生态JS验证用户名是否存在
May 26 Javascript
详解探索 vuex 2.0 以及使用 vuejs 2.0 + vuex 2.0 构建记事本应用
Jun 16 Javascript
深入理解vuex2.0 之 modules
Nov 20 Javascript
js正则取值的结果数组调试方法
Oct 10 Javascript
微信小程序如何获取手机验证码
Nov 04 Javascript
Typescript 中的 interface 和 type 到底有什么区别详解
Jun 18 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
yii2.0之GridView自定义按钮和链接用法
2014/12/15 PHP
PHP获取ip对应地区和使用网络类型的方法
2015/03/11 PHP
一段非常简单的让图片自动切换js代码
2006/11/10 Javascript
选择TreeView控件的树状数据节点的JS方法(jquery)
2010/02/06 Javascript
利用JQuery动画制作滑动菜单项效果实现步骤及代码
2013/02/07 Javascript
js文件Cookie存取值示例代码
2014/02/20 Javascript
代码获取历史上的今天发生的事
2014/04/11 Javascript
常用jQuery代码分享
2015/07/14 Javascript
javascript添加前置0(补零)的几种方法
2017/01/05 Javascript
Angular的$http的ajax的请求操作(推荐)
2017/01/10 Javascript
React Native预设占位placeholder的使用
2017/09/28 Javascript
详解如何用babel转换es6的class语法
2018/04/03 Javascript
vue自定义filters过滤器
2018/04/26 Javascript
jQuery实现点击自身以外区域关闭弹出层功能完整示例【改进版】
2018/07/31 jQuery
JQuery实现ajax请求的示例和注意事项
2018/12/10 jQuery
ES6 Symbol数据类型的应用实例分析
2019/06/26 Javascript
微信小程序实现上拉加载功能
2019/11/20 Javascript
ES6字符串的扩展实例
2020/12/21 Javascript
Python 时间处理datetime实例
2008/09/06 Python
简单的通用表达式求10乘阶示例
2014/03/03 Python
对python-3-print重定向输出的几种方法总结
2018/05/11 Python
django 外键model的互相读取方法
2018/12/15 Python
Python GUI编程完整示例
2019/04/04 Python
python函数修饰符@的使用方法解析
2019/09/02 Python
python scipy卷积运算的实现方法
2019/09/16 Python
python连接手机自动搜集蚂蚁森林能量的实现代码
2021/02/24 Python
深深扎根运动世界的生活品牌:Tillys
2017/10/30 全球购物
解释DataSet(ds) 和 ds as DataSet 的含义
2014/07/27 面试题
实习单位接收函模板
2014/01/10 职场文书
《中国的气候》教学反思
2014/02/23 职场文书
旷课检讨书500字
2014/10/14 职场文书
2014小学教师年度考核工作总结
2014/12/03 职场文书
武夷山导游词
2015/02/03 职场文书
2015年领班工作总结
2015/04/29 职场文书
2015年机械设备管理工作总结
2015/05/04 职场文书
MySQL之select、distinct、limit的使用
2021/11/11 MySQL