请选择 进入手机版 | 继续访问电脑版
查看: 1109|回复: 55

[教程下载] HTML5—CSON实现3D旋转涂鸦效果

[复制链接]
最佳答案
0 
guofinni 发表于 2017-1-10 13:40:47 | 显示全部楼层 |阅读模式
功能说明:
通过鼠标移动,实时绘制出3d旋转的线条。
兼容IE 5 6 7 8 9 10 firefox chrome

效果预览:
QQ截图20170105111221.jpg

实现原理:
  我们通过把点绕不同椭圆轨迹的运动实现任意线的3d旋转。当按下鼠标左键在画板上绘制时,每个点根据初始位置,确定其椭圆轨迹大小,以相同的速率改变每个点的位置,从而实现整条曲线的3d旋转。

代码分析:
  1. init:function(id,options){//初始化函数
  2.                 options=options||{};
  3.                 this.container=document.getElementById(id||'container');
  4.                 this.centerLeft=this.container.clientWidth/2;//原点的left值
  5.                 this.centerTop=this.container.clientHeight/2;    //原点的top值
  6.                 this.maxA=options.maxA||300;//旋转椭圆轨迹的横半轴长
  7.                 this.maxB=options.maxB||1;//旋转椭圆轨迹的竖半轴长
  8.                 this.ballMargin=options.ballMargin||20;//绘画时小球与小球间间的距离
  9.                 this.arr=[];//保存画板上所有小球的数组
  10.                 this._id;//计时器Id
  11.                
  12.                 this.containerPos=getContainerPos(this.container);//容器位置
  13.                
  14.                 bindHandler.call(this);
  15.                 this.run();
  16.             }
复制代码

 先看初始化需要哪些值,要使所有点实现椭圆旋转,首先确定椭圆圆心的left和top值,这里椭圆圆心取容器的中点位置。之后确定的是椭圆轨迹的横半轴和竖半轴长,还有绘制时小球与小球之间的距离(ballMargin),距离越大,每个小球之间间隙越宽。另外我们需要获取容器的位置,为获取鼠标在容器内的位置打下基础。

  1. var getContainerPos=function(container){//获取容器位置
  2.         
  3.             var left=0;
  4.             var top=0;
  5.             while(container.offsetParent){
  6.                 left+=container.offsetLeft;
  7.                 top+=container.offsetTop;
  8.                 container=container.offsetParent;
  9.                
  10.             }
  11.             return [left,top];

  12.         }
复制代码

  该方法在很多时候都会需要用到,它循环获取有定位的父对象,累计出容器在页面的位置,返回结果。注意offsetParent是有定位的父对象,而不是单纯的父对象,没有的话就为window。该方法作为私有方法存在。

  1. var bindHandler = function() {//绑定容器的事件处理程序
  2.                 var self = this;

  3.                 this.container.onmousedown = function(eve) {
  4.                     add = true;
  5.                 }

  6.                 this.container.onmousemove = function(eve) {

  7.                     window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty(); //取消浏览器图片选择
  8.                     if (add === true) {
  9.                         eve = eve || window.event;
  10.                         var pageX = eve.pageX || eve.clientX + document.documentElement.scrollLeft - document.documentElement.clientLeft;
  11.                         var pageY = eve.pageY || eve.clientY + document.documentElement.scrollTop - document.documentElement.clientTop;
  12.                         
  13.                         var mouseX = pageX - self.containerPos[0] - self.centerLeft;
  14.                         var mouseY = -(pageY - self.containerPos[1]) + self.centerTop;

  15.                         if (Math.abs(mouseX - preX) > self.ballMargin || Math.abs(mouseY - preY) > self.ballMargin) {
  16.                             self.addElem(mouseX, mouseY, 'ball/ball2.gif');
  17.                             preX = mouseX;
  18.                             preY = mouseY;
  19.                         }


  20.                     }

  21.                 }
  22.                 this.container.onmouseup = function(eve) {
  23.                     add = false;
  24.                     preX = 0;
  25.                     preY = 0;

  26.                 }
  27.             };
复制代码

  之后要实现鼠标拖动绘制,必须为鼠标按下,鼠标移动,鼠标松开都绑定事件处理程序。主要讲讲mousemove里的处理,首先由于在鼠标拖动时浏览器会默认选择图片(使图片上面有一层蓝色),所以我们通过removeAllRanges或empty方法取消选择,使我们的图片显示正常。之后,如果鼠标是按下状态(add句柄为true),则再判断上一次绘制小球的位置,如果位置大于我们之前设定的小球间隔,则绘制小球,并保存本次绘制小球的位置,为下次绘制所用。这样就使小球之间保持适当间隔而不至于太密。

  1. addElem:function(initX,initY,src){//添加小球
  2.                 var newElem=new Image();
  3.                 newElem.src=src;
  4.                 var self=this;
  5.                 self.container.appendChild(newElem);
  6.                
  7.                 if(newElem.complete){;
  8.                     imgLoad.call(this,newElem,initX,initY)();
  9.                 }
  10.                 else{
  11.                     newElem.onload=imgLoad.call(this,newElem,initX,initY);
  12.                 }
  13.                
  14.         },
复制代码



  绘制小球的时候,会调用addElem方法,该方法生成小球对象,添加到文档。注意在IE8及以下版本,由于浏览器的缓存原因,只设置图片的onload的话,会不能执行onload处理程序,解决方法是通过complete方法先判断是否已再缓存中,如果是立刻执行处理程序,如果不在再绑定onload处理程序。

  1. var imgLoad=function(newElem,initX,initY){//图片加载完成事件处理程序
  2.             var self=this;
  3.             return function(){
  4.                
  5.                 initX<0?newElem.angle=Math.PI:newElem.angle=0;//小球被添加的位置,如果小球被添加在负区域,初始旋转角度为pai,否则为0
  6.                 newElem._x=newElem._initX=initX;
  7.                 newElem._y=newElem._initY=initY;
  8.                 newElem._preSin=0;                                
  9.                
  10.                 newElem._a=Math.abs(initX);                        //该小球旋转轨迹的a值
  11.                 newElem._b=self.maxB;                            //该小球旋转轨迹的b值
  12.                     
  13.                 //根据小球初始位置和上一个小球的位置,设置小球的层级
  14.                 var preElem=self.arr[self.arr.length-1];
  15.                 if(preElem){
  16.                     var preZin=parseInt(preElem.style.zIndex);
  17.                     var preX=preElem._initX;
  18.                     preX>=initX?newElem.style.zIndex=preZin+1:newElem.style.zIndex=preZin-1;
  19.                 }
  20.                 else{
  21.                     newElem.style.zIndex=zIn;
  22.                 }

  23.                 newElem._initWidth=newElem.clientWidth;
  24.                 newElem._initHeight=newElem.clientHeight;
  25.                 self.arr.push(newElem);
  26.             }

  27.         };
复制代码

  现在看看图片加载完成的事件处理程序,每个小球根据其初始位置设置旋转的横轴长和初始角度,横轴长为小球x值的绝对值,竖轴长则全部一样,这里建议竖轴长设置为较小值,使小球运动轨迹的上下浮动不至于过大,保证视觉上的合理性。另外需要重点注意的是,每个小球添加时的zIndex值和该小球初始的x坐标值以及上一次添加的小球的x坐标值有关。如果添加的小球x值比上次小球添加时的x值小,则证明该小球更接近原点,因此视觉上应该更靠前,zIndex比上个小球的zIndex增加1,同理,若比上次小球x值大,则小球的zIndex值减少1.如果是第一个小球,则取默认zIndex(代码中的zIn)。

  1. run:(function(){
  2.    
  3.             var updatePos=function(elem,angle,a,b,centerLeft,centerTop){//update每次小球位置 参数:小球对象,增加的角度,小球轨迹a值,小球轨迹b值
  4.                 elem.angle+=angle;
  5.             
  6.                 (elem.angle>2*Math.PI)&&(elem.angle-=2*Math.PI);//使小球角度介于0-pai之间,方便计算
  7.                 elem._x=a*Math.cos(elem.angle);                //根据角度计算x值
  8.                 elem._y=b*Math.sin(elem.angle)+elem._initY;//根据角度计算y值
  9.                
  10.                 elem.style.left=elem._x+centerLeft-elem._initWidth/2+'px';
  11.                 elem.style.top=-elem._y+centerTop-elem._initHeight/2+'px';
  12.                 //与小球上次的sin值比较,如果与本次sin值互为正负,则小球zIndex值取反(这样的目的是每次经过x轴时使小球的层级取反)
  13.                 if(Math.sin(elem.angle)*elem._preSin<0){                        
  14.                     elem.style.zIndex*=-1;
  15.                 }
  16.                 elem._preSin=Math.sin(elem.angle);    //记录小球每次的sin值,用于下次小球层级计算
  17.             
  18.             }
  19.             
  20.             return function(){
  21.                 var self=this;
  22.                 this._id=window.setTimeout(function(){
  23.                     for(var i=0,len=self.arr.length;i<len;i++){//循环遍历更新所有小球的位置
  24.                         updatePos(self.arr[i],Math.PI/100,self.arr[i]._a,self.arr[i]._b,self.centerLeft,self.centerTop);
  25.                     }
  26.                     self._id=window.setTimeout(arguments.callee,50);
  27.                 },50);
  28.             
  29.             };
复制代码

  当一张张图片被添加之后,我们就要通过定时器,不断改变小球位置,使它们以不同的椭圆轨迹,以相同的角速度旋转。关于椭圆轨迹的计算问题,上一篇文章《椭圆旋转相册》中描述过,现在再简短描述一次。椭圆的标准方程为:


W020100905850347446352.gif           W020100905850347441147.gif

,由于需要处理的是旋转,所以我们希望把对x,y的处理转换成对旋转角度的处理,因此x,y坐标可以表示为:x=a*cosα , y=b*sinα 。所以我们每次增加角速度α,就可以映射到xy直角坐标系中,实现旋转。还需要注意的是由于小球每次经过x轴,小球的层级就会相反(意思是原来小球A在B前面,经过x轴后,B在A前面),于是我们需要记录小球上一次的位置的sin值,并和本次的sin值比较,如果相乘小于0,则表示小球正在经过x轴,此时就取反小球的层级zIndex。run方法调用后,每次遍历数组,更新小球位置。
  1. var r3d=new rotate3dDraw();
复制代码
  最后是调用方法,不传值的话都取默认值。
游客,如果您要查看本帖隐藏内容请回复


打赏列表共打赏了0次

cry
还木有人打赏~
最佳答案
0 
linchang123 发表于 2017-1-10 15:08:00 | 显示全部楼层
阿道夫噶三大阿斯顿发啊发生的f
最佳答案
0 
42222222 发表于 2017-1-11 09:57:43 | 显示全部楼层
66666666666666
最佳答案
0 
42222222 发表于 2017-1-11 10:30:56 | 显示全部楼层
6666666666666666666666
最佳答案
0 
lyxq 发表于 2017-1-11 18:37:20 | 显示全部楼层
从入门到实战33集微信小程序视频教程
最佳答案
0 
wl8903 发表于 2017-1-11 18:38:12 | 显示全部楼层
学习啦·················
您需要登录后才可以回帖 登录 | 注册

本版积分规则

推荐阅读 More>

© 2001-2015 9秒社团

合作伙伴

公司简介 | 联系方式
COPYRIGHT©2015 ZHONGQINGLONGTU NETWORK CO.LTD ALL RIGHTS RESERVED.ICP备11023195号-4
北京中清龙图网络技术有限公司
返回顶部 返回列表