*每当在DOM浏览器中增加动态效果时,使用强大的transform和transition,总是很酸爽。抛开css,使用js操作transform还真的有点复杂,涉及到线性代数中的矩阵,但是js操作又不可避免的会用到。俗话说,山水有相逢,早日学会,早日总结,方便以后用到。今天就与大家分享一下,transform的注意事项以及transform矩阵操作的一些技巧。
*首先说一些小的注意事项,硬菜在后面!
1.js操作transition时需使用驼峰命名增加前缀:
div.style.WebkitTransform = div.style.transform = "rotate(90deg)";
2.多个transition操作的执行顺序:先写的后后执行
以下以两个div为例,点击后执行不同的过渡效果:
div[0].addEventListener('touchend', function(e) { this.style.WebkitTransform = this.style.transform = "scale(.5) translateX(100px)"; }); div[1].addEventListener('touchend', function(e) { this.style.WebkitTransform = this.style.transform = "translateX(100px) scale(.5)"; });
初始 效果:
点击后的效果:
页面代码如下,可自己拎下来运行:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,user-scalable=no" /> <title>执行顺序</title> <style type="text/css"> #box { 100px; border: 1px solid #000; padding: 100px; } .div { 100px; height: 100px; margin: 10px 0; background: red; transition: 3s; -webkit-transform-origin: 0 0; transform-origin: 0 0; } </style> <script type="text/javascript"> document.addEventListener('touchstart', function(e) { e.preventDefault(); }); window.onload = function(){ var div = document.querySelectorAll('.div'); div[0].addEventListener('touchend', function(e) { this.style.WebkitTransform = this.style.transform = "scale(.5) translateX(100px)"; }); div[1].addEventListener('touchend', function(e) { this.style.WebkitTransform = this.style.transform = "translateX(100px) scale(.5)"; }); }; </script> </head> <body> <div id="box"> <div class="div"></div> <div class="div"></div> </div> </body> </html>
3.移动端在操作结点位置时,尽量使用translate,而操作left或margin等都会引起页面回流(reflow),我们要做的就是尽可能地避免回流,尽可能的减少重绘。
举个栗子使用translate模拟垂直滚动条:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>垂直滚动</title> <meta name="viewport" content = "width=device-width,user-scalable=no"> <style type="text/css"> body { margin: 0; } html, body { overflow: hidden; height: 100%; position: relative; } header{ height: 40px; background: #000; color: #fff; text-align: center; font-size: 20px; line-height: 40px; } #wrap { position: absolute; left: 0; right: 0; top: 40px; bottom: 0px; overflow: hidden; } #list { margin: 0; padding: 0; list-style: none; } #list li { height: 30px; border-bottom: 1px solid #000; line-height: 30px; text-indent: 20px; } </style> <script type="text/javascript"> document.addEventListener("touchstart",function(e){ e.preventDefault(); }); function setListInner(){ var list = document.querySelector('#list'); var inner = ""; for(var i = 0; i < 100; i++){ inner += "<li>这是第"+i+"个li</li>" } list.innerHTML = inner; } window.onload = function(){ setListInner(); var wrap = document.querySelector('#wrap'); var list = document.querySelector('#list'); var startPoint = 0; var startEl = 0; var elTranslateY = 0; list.addEventListener('touchstart', function(e) { startPoint = e.changedTouches[0].pageY; startEl = elTranslateY; }); list.addEventListener('touchmove', function(e) { var nowPoint = e.changedTouches[0].pageY; var dis = nowPoint - startPoint; elTranslateY = startEl + dis; list.style.WebkitTransform = list.style.transform = "translateY("+elTranslateY+"px)"; }); }; </script> </head> <body> <header>垂直滚动</header> <div id="wrap"> <ul id="list"></ul> </div> </body> </html>
4.矩阵操作
大家都知道当使用js获取transform时获取的是一个矩阵:
div[0].style.WebkitTransform = div[0].style.transform = "scale(.5) translateX(100px)"; //最终样式才能获取到transform的矩阵参数字符串 console.log(getComputedStyle(div[0])["transform"]); // matrix(0.5, 0, 0, 0.5, 50, 0) console.log( typeof (getComputedStyle(div[0])["transform"])); // string console.log(div[0].style.transform); // scale(0.5) translateX(100px) console.log(typeof (div[0].style.transform)); // string
*矩阵参数对应的变量:
matrix( a, b, c, d, e, f ),基础值为:
matrix( 1, 0, 0, 1, 0, 0 )
基础效果如下:
1.改变位移:
//位移: //x为轴位移量,y为y轴位移量,可使用负数 //x轴位移 = e + x; //y轴位移 = f + y; //如下为左移100像素,下移50像素 div[0].style.transform = "matrix(1, 0, 0, 1, -100, 50)";
改变位移效果:
2.缩放:
(1) x轴缩放:操作变量a,c,e
//x轴: //a = a*x; //c = c*x; //e = e*x;
//x为缩放倍数
div[0].style.transform = "matrix(.5, 0, 0, 1, 0, 0)";
x轴缩放0.5效果:
(2)y轴缩放:操作变量b,d,f
//y轴: //b = b*x; //d = d*x; //f = f*x; div[0].style.transform = "matrix(1, 0, 0, .5, 0, 0)";
y轴缩放后效果:
由此就可以解释,在使用css命令时的执行顺序问题:
//先执行matrix(.5, 0, 0, 1, 0, 0) //后执行matrix(.5, 0, 0, 1, 100, 0) div[0].style.transform = "scaleX(.5) translateX(100px)"; //先执行matrix(1, 0, 0, 1, 100, 0) //后执行matrix(.5, 0, 0, 1, 50, 0) div[0].style.transform = "translateX(100px) scaleX(.5)";
3.斜切:
(1)x轴斜切:
//x轴斜切30度:单位(deg) //操作参数c,修改参数时需要转化为弧度 //弧度 = Math.tan(角度/180*Math.PI); div[0].style.transform = "matrix(1, 0, "+Math.tan(30/180*Math.PI)+", 1, 0, 0)";
效果如下:
(2)y轴斜切:
//y轴斜切30度:单位(deg) //操作参数b,修改参数时需要转化为弧度 div[0].style.transform = "matrix(1,"+Math.tan(30/180*Math.PI)+",0, 1, 0, 0)";
效果如下:
4.旋转rotate:
//旋转: //修改a,b,c,d四个参数 var a = Math.cos(45/180*Math.PI); var b = Math.sin(45/180*Math.PI); var c = -Math.sin(45/180*Math.PI); var d = Math.cos(45/180*Math.PI); div[0].style.transform = "matrix("+ a +","+ b +","+ c +","+ d +",0,0)";
效果如下:
5.transform操作函数封装
若现在有这么一个需求,每次点击div后,使其旋转30deg,那么我们是无法直接操作transform完成的,通过js修改操作css的transform无法记录当前的状态(旋转角度等)。因此需要转换思想进行函数封装,封装的原理是添加自定义属性,再利用自定义属性为相应结点添加transform。
function cssTransform(element, attr, val){ if(!element.transform){ element.transform = {}; } if(typeof val === "undefined"){ if(typeof element.transform[attr] === "undefined"){ switch(attr){ case "scale": case "scaleX": case "scaleY": case "scaleZ": element.transform[attr] = 100; break; default: element.transform[attr] = 0; } } return element.transform[attr]; } else { element.transform[attr] = val; var transformVal = ""; for(var s in element.transform){ switch(s){ case "scale": case "scaleX": case "scaleY": case "scaleZ": transformVal += " " + s + "("+(element.transform[s]/100)+")"; break; case "rotate": case "rotateX": case "rotateY": case "rotateZ": case "skewX": case "skewY": transformVal += " " + s + "("+element.transform[s]+"deg)"; break; default: transformVal += " " + s + "("+element.transform[s]+"px)"; } } element.style.WebkitTransform = element.style.transform = transformVal; } }
注意!如果要获取transform相关的属性,那transform相关的设置 也必须是通过该方法设置的!
利用封装的函数,就可以完成刚才的需求。完整的代码如下,可自行运行查看效果:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,user-scalable=no" /> <title>点击旋转30deg</title> <style type="text/css"> #box { 100px; height: 100px; border: 1px solid #000; padding: 100px; } #div { height: 100px; background: red; transition: .5s; } </style> <script type="text/javascript"> function cssTransform(element, attr, val){ if(!element.transform){ element.transform = {}; } if(typeof val == "undefined"){ if(!element.transform[attr]){ switch(attr){ case "scale": case "scaleX": case "scaleY": case "scaleZ": element.transform[attr] = 100; break; default: element.transform[attr] = 0; } } return element.transform[attr]; } else { element.transform[attr] = val; var transformVal = ""; for(var s in element.transform){ switch(s){ case "scale": case "scaleX": case "scaleY": case "scaleZ": transformVal += " " + s + "("+(val/100)+")"; break; case "rotate": case "rotateX": case "rotateY": case "rotateZ": case "skewX": case "skewY": transformVal += " " + s + "("+val+"deg)"; break; default: transformVal += " " + s + "("+val+"px)"; } } element.style.WebkitTransform = element.style.transform = transformVal; } } window.onload = function(){ var div = document.querySelector('#div'); div.addEventListener('touchend', function(e) { var deg = cssTransform(this, "rotate"); deg += 30; cssTransform(this, "rotate", deg); }); }; </script> </head> <body> <div id="box"> <div id="div"></div> </div> </body> </html>