移动端二三事【三】:transform的矩阵(matrix)操作、transform操作函数及注意事项

*每当在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>
原文地址:https://www.cnblogs.com/pomelott/p/7784684.html