自上次发布用纯原生态javascript+css3 写的3D魔方动画旋扭特效后,说要加上事件的,结果一直忙直到现在才得以有空,故赶紧抽时间赶出这篇文章。上次也因为时间原因只是粘出代码,没有详细解释一下3D魔方具体的实现原理和思想,我个人觉得程序语言只是实现效果的一种工具而已,而最具灵魂性的东西就是实现的原理和算法,只要掌握原理和算法,才能举一反三,才能更灵活的变通,才能如何加上自己的东西进行进一步的改善和创新。故这篇文章不仅要给出代码还会尽量的用文字来阐述一下实现的原理。现实中魔方就是由一个个的小的立方体组成一个大的立方体,共六个面,六中颜色,然后旋转其中的指定的可移动的面来实现不通颜色面的组合。下面我们就按下面的几个步骤来阐述一下。

第一步:创建单个小立方体对象

 //单个立方体对象
            function Cube(opts) {
                opts = opts || {};
                this.parent = opts.parent; //插入到哪里
                this.browserPrefix = opts.browserPrefix;
                this.width = opts.width;
                this.height = opts.height;
                this.cubZ = opts.cubZ;
                this.face = opts.face;
                this.row = opts.row;
                this.column = opts.column;
                this.index=opts.index;
                this.offsetX = this.column * (this.width + opts.marginX); //
                this.offsetY = this.row * (this.height + opts.marginY); //
                this.offsetZ = this.face * (this.cubZ + opts.marginZ); //
                this.positiveZ = this.cubZ / 2;
                this.negativeZ = -this.cubZ / 2;
                this.cubFaceInfo = opts.cubFaceInfo;
                this.dimension = opts.dimension;
                this.centerX = (this.dimension * this.width + (this.dimension - 1) * opts.marginX) / 2;
                this.centerY = (this.dimension * this.height + (this.dimension - 1) * opts.marginY) / 2;
                this.centerZ = (this.dimension * this.cubZ + (this.dimension - 1) * opts.marginZ) / 2;
                this.translateX = this.offsetX - this.centerX; //把中心点设为原点
                this.translateY = this.offsetY - this.centerY; //
                this.translateZ = this.cubZ / 2 + this.offsetZ - this.centerZ; //offsetZ按上面计算应该跟x,y在一个平面上即后面,但实际上由于要形成立方体,在Z轴上已经后退了cubZ/2个距离,故为和上面保持一致在一个面上,这里需要再加回cubZ/2个距离,使默认的xyz原点都在一个平面上即立方体后面左上角三维坐标系,以这个点作为参考点平移和设置旋转原点
                this.cubeFace = [];
                this.rotateTransfrom = "";
                this.init();

            }
            Cube.prototype = {
                init: function () {
                    this.createCubeBox();
                    this.createFront();
                    this.createBack();
                    this.createTop();
                    this.createBottom();
                    this.createLeft();
                    this.createRight();

                },
                createCubeBox: function () {
                    this.Box = document.createElement('div');
                    this.Box.style.width = this.width + "px";
                    this.Box.style.height = this.height + "px";
                    this.Box.style.left = "50%";
                    this.Box.style.top = "50%";
                    this.Box.style.position = "absolute";
                    this.Box.style[this.browserPrefix + "TransformStyle"] = "preserve-3d";
                    this.Box.style[this.browserPrefix + "Perspective"] = "0";
                    //                    this.Scene.style[this.browserPrefix + "backfaceVisibility"] = "hidden";
                    this.intalTransform = "translateZ(" + this.translateZ + "px) translateX(" + this.translateX + "px) translateY(" + this.translateY + "px)";
                    this.Box.style[this.browserPrefix + "Transform"] = this.intalTransform;
                    this.Box.style[this.browserPrefix + "TransformOrigin"] = "" + (-this.translateX) + "px " + (-this.translateY) + "px " + (-this.translateZ) + "px";
                    this.Box.index=this.index;
                    this.parent.appendChild(this.Box);
                    this.x = window.getComputedStyle(this.Box).getPropertyValue('left');
                    this.y = window.getComputedStyle(this.Box).getPropertyValue('top');
                    this.matrix3d = window.getComputedStyle(this.Box).getPropertyValue('transform');
                },
                createFace: function () {
                    var face = document.createElement('div');
                    face.style.margin = 0;
                    face.style.position = "absolute";
                    face.style.width = this.width + "px";
                    face.style.height = this.height + "px";
                    face.style.textAlign="center";
                    face.style.lineHeight=this.height + "px";
                   // face.innerHTML=this.index;
                    return face;
                },
                createFront: function () {
                    var face = this.createFace();
                    face.style[this.browserPrefix + "Transform"] = "translateZ(" + this.positiveZ + "px) ";
                    face.translateZ = this.positiveZ; //给生成控制面板用
                    this.cubeFace.push(face);
                    this.front = face;
                    this.Box.appendChild(face);
                },
                createBack: function () {
                    var face = this.createFace();
                    face.style[this.browserPrefix + "Transform"] = "translateZ(" + this.negativeZ + "px) ";
                    face.translateZ = this.negativeZ; //给生成控制面板用
                    this.cubeFace.push(face);
                    this.back = face;
                    this.Box.appendChild(face);
                },
                createTop: function () {
                    var face = this.createFace();
                    face.style[this.browserPrefix + "Transform"] = "rotateX(90deg) translateZ(" + this.positiveZ + "px) ";
                    face.translateZ = this.positiveZ; //给生成控制面板用
                    this.cubeFace.push(face);
                    this.top = face;
                    this.Box.appendChild(face);
                },
                createBottom: function () {
                    var face = this.createFace();
                    face.style[this.browserPrefix + "Transform"] = "rotateX(90deg) translateZ(" + this.negativeZ + "px) ";
                    face.translateZ = this.negativeZ; //给生成控制面板用
                    this.cubeFace.push(face);
                    this.bottom = face;
                    this.Box.appendChild(face);
                },
                createLeft: function () {
                    var face = this.createFace();
                    face.style[this.browserPrefix + "Transform"] = "rotateY(90deg) translateZ(" + this.negativeZ + "px) ";
                    face.translateZ = this.negativeZ; //给生成控制面板用
                    this.cubeFace.push(face);
                    this.left = face;
                    this.Box.appendChild(face);
                },
                createRight: function () {
                    var face = this.createFace();
                    face.style[this.browserPrefix + "Transform"] = "rotateY(90deg) translateZ(" + (this.positiveZ) + "px) ";
                    face.translateZ = this.positiveZ; //给生成控制面板用
                    this.cubeFace.push(face);
                    this.right = face;
                    this.Box.appendChild(face);
                }

            }
View Code

这段代码是用于创建魔方的单个小立方体的对象,这里小立方体由六个DIV元素组成,分别标识为前(front),后(back),左(left),右(right),上(top),下(bottom)六个对象;

六个面放在一个设置了TransformStyle样式的BoxDiv中形成一个3D效果的小立方体,然后再根据传入参数通过Transform,TransformOrigin计算和设置该立方体在整个魔方中的位置。

第二步:给魔方的6个面着色

对于上一步创建的小立方体是没有着色的,因为对于要着色的面必须是对应魔方的对外的六个面上的才需要着色,而正常的逻辑是对于小立方体而言,处于魔方定点上的四个立方体有三个面要着色,棱上的立方体需要两个面着色,而在面中间的立方体则只需要一个面着色,然而按这个逻辑去着色是很麻烦的事情,要找出四个顶点上的,棱上的,面中间的立方体,而对于不同阶数魔方来说,棱上和面中间的立方体数量也是不同的,计算起来会比较复杂,所以这里着色采用的另一种方式:

 drawBackGroundColor: function () {
                    for (var face in this.cubFaceInfo) {
                        if (face == "inner") {
                            this.setInnerBKColor(this.cubFaceInfo[face].backGround);
                        }
                        else {
                            var cube = this.getCubesByFace(face);
                            for (var i = 0, len = cube.length; i < len; i++) {
                                var controlPanel = this.getControlPanel(cube[i].Box, cube[i][face]);
                                this.createController(controlPanel, face, cube[i].index); //创建对应的控制面板
                                cube[i][face].style.background = this.cubFaceInfo[face].backGround;
                                this.bindMouseEvent(cube[i][face], face); //绑定触发和解除控制面板的事件
                            }
                        }
                    }

                }
View Code

 

从上面代码看出,魔方有6个面,而组成它的小立方体也是6面,所以比如我要着色魔方的前面(front)时我只要找到组成魔方前面的这些立方体对每个立方体的前面(front)着对应的色即可,其他的面也是照此类同,至于立方体的处于魔方内部的其他面上的颜色则便是处了已被着色面的剩余的面,给他们统一着一个色便可。但是如何获取指定面上所有立方体呢?看下面的代码:

getCubesByFace: function (face) {
                    switch (face) {
                        case "front": return this.getZFace(this.dimension);
                        case "back": return this.getZFace(1);
                        case "left": return this.getXFace(1);
                        case "right": return this.getXFace(this.dimension);
                        case "top": return this.getYFace(1);
                        case "bottom": return this.getYFace(this.dimension);
                    }
                }
View Code

 

这里的方法里又涉及到了我个人认为在整个系统里面比较重要的几个方法:

   getZFace: function (zIndex) {
                    var zFace = [];
                    if (zIndex < 1 || zIndex > this.dimension)
                        return null;
                    for (var i = (zIndex - 1) * this.faceCount; i < zIndex * this.faceCount; i++) {
                        zFace.push(this.cubes[i]);
                    }
                    return zFace;
                },
                getXFace: function (xIndex) {
                    var xFace = [];
                    if (xIndex < 1 || xIndex > this.dimension)
                        return null;
                    for (var i = 0; i < this.count; i++) {
                        if (i % this.dimension == 0)
                            xFace.push(this.cubes[i + xIndex - 1]);
                    }
                    return xFace;
                },
                getYFace: function (yIndex) {
                    var yFace = [];
                    if (yIndex < 1 || yIndex > this.dimension)
                        return null;
                    for (var i = 0; i < this.count; i++) {
                        if (i % this.faceCount == (yIndex - 1) * this.dimension) {
                            for (var j = 0; j < this.dimension; j++)
                                yFace.push(this.cubes[i + j]);
                        }
                    }
                    return yFace;
                }
View Code

 

getZFace:指的是在Z轴上指定索引的面上的所有立方体,getYFace:指的是在Y轴上指定索引的面上的所有立方体,getXFace:指的是在X轴上指定索引的面上的所有立方体,比如对于一个3阶的魔方对应的索引都是从1到3,如下图所示:

第三步:创建面旋转时动画效果,绑定动画结束事件

  findKeyframesRule: function (rule) {
                    var ruleName = this.browserPrefix == "" ? "KEYFRAMES_RULE" : this.browserPrefix.toUpperCase() + "_KEYFRAMES_RULE";
                    var ss = document.styleSheets;
                    for (var i = 0; i < ss.length; ++i) {
                        for (var j = 0; j < ss[i].cssRules.length; ++j) {
                            if (ss[i].cssRules[j].type == window.CSSRule[ruleName] && ss[i].cssRules[j].name == rule) { return ss[i].cssRules[j]; }
                        }
                    }
                    return null;
                },
                createKeyframesRule: function (rule, startStatus, endStatus) {
                    var prefix = this.browserPrefix == "" ? "" : "-" + this.browserPrefix + "-";
                    var sheet;
                    if (document.styleSheets.length < 1)
                        sheet = this.createSheets();
                    else
                        sheet = document.styleSheets[0];
                    var selectorText = "@" + prefix + "keyframes " + rule;
                    var cssText = "0% { " + prefix + "transform: " + startStatus + "; } 100% { " + prefix + "transform: " + endStatus + "; }"
                    if (sheet.insertRule) {
                        sheet.insertRule(selectorText + "{" + cssText + "}", 0);
                    } else if (sheet.addRule) {//兼容IE
                        sheet.addRule(selectorText, cssText, 0);
                    }
                },
                removeKeyframeRule: function (keyframes) {
                    var length = keyframes.cssRules.length;
                    var keyframeString = [];
                    for (var i = 0; i < length; i++) {
                        keyframeString.push(keyframes.cssRules[i].keyText);
                    }
                    //移除动画帧规则
                    for (var i = 0, j = length; i < j; i++) {
                        if (this.browserPrefix == "webkit" || this.browserPrefix == "Moz")
                            keyframes.deleteRule(keyframeString[i]);
                        else
                            keyframes.deleteRule(i); //兼容IE
                    }
                },
                changeAnimationStatus: function (animationName, startStatus, endStatus) {
                    var keyframes = this.findKeyframesRule(animationName);
                    this.removeKeyframeRule(keyframes);
                    //重新设置帧规则
                    var prefix = this.browserPrefix == "" ? "" : "-" + this.browserPrefix + "-";
                    keyframes.appendRule("0% { " + prefix + "transform: " + startStatus + "; }");
                    keyframes.appendRule("100% { " + prefix + "transform: " + endStatus + "; }");
                },
                createSheets: function () {
                    // 创建 <style> 标签
                    var style = document.createElement("style");
                    // 可以添加一个媒体(/媒体查询,media query)属性
                    // style.setAttribute("media", "screen")
                    // style.setAttribute("media", "only screen and (max-width : 1024px)")
                    // 对WebKit hack :(
                    style.appendChild(document.createTextNode(""));
                    // 将 <style> 元素加到页面中
                    document.head.appendChild(style);
                    return style.sheet;
                }
View Code

 

这段代码分别对应的是查找帧规则,创建帧规则,移除帧规则,更改帧规则和动态创建样式表方法,说明一下这里的旋转时所采用的动画是采用动画帧的方式实现的,所以要通过以上方法,动态为每一此旋转的动作创建或更改帧规则来实现动画的效果。

 timerFun: function () {
                    var _this = cache.magicCube;
                    if (_this.isRunning >= _this.dimension) {
                        for (var i = 0, len = _this.cubeMoveQueue.length; i < len; i++) {
                            var animation = _this.cubeMoveQueue.shift();
                            animation.cube.Box.style[_this.browserPrefix + "Animation"] = animation.rule + " " + _this.delay + "s linear 1"; // Chrome, Safari 和 Opera 代码
                        }
                    }
                }
View Code

 

这个方法就是用于从对应的动画队列中获取要播放的动画对象,应用动画规则,开始动画的播放。

bindAnimationEvent: function () {
                    var loopMove = function () {
                        cache.magicCube.isRunning--; //由于按组转动,顾要等组成员都完成再进行新的动画
                        if (cache.magicCube.isRunning == 0)
                            cache.magicCube.animationEnd();
                    }
                    for (var i = 0; i < this.count; i++) {

                        this.prefixedEvent(this.cubes[i].Box, "AnimationEnd", loopMove, true);
                    }
                    cache.magicCube = this; //缓存,避免内存泄露
                }
View Code

 

这个方法是绑定的动画播放结束事件。

第四步:旋转魔方的指定面

 moveMagicCube: function () {
                    if (this.cubes.length < 1) return;
                    //var cubes = this.getYFace(2);
                    //for (var i = 0, len = cubes.length; i < len; i++) {
                    //    cubes[i].Box.className = "rotate";
                    //}
                    //随机产生3D转动方向
                    this.isRunning = 0;
                    var direct = this.random(1, 3), rotateDirect = "", getFaceFun;
                    //  direct=3;
                    switch (direct) {
                        case 1: rotateDirect = "rotateX"; getFaceFun = this.getXFace; break;
                        case 2: rotateDirect = "rotateY"; getFaceFun = this.getYFace; break;
                        case 3: rotateDirect = "rotateZ"; getFaceFun = this.getZFace; break;
                    }
                    this.rotateFace = rotateDirect;
                    this.cubeRotateStatus = [];
                    for (var i = 1; i <= this.dimension; i++) {
                        var status = this.random(0, 2);
                        this.cubeRotateStatus.push(status);
                        switch (status) {
                            case 0: break; //不转动
                            case 1: this.rotateBox(this.angle, rotateDirect, i, getFaceFun.call(this, i)); break; //正向转动90
                            case 2: this.rotateBox(-this.angle, rotateDirect, i, getFaceFun.call(this, i)); break; //反向转动90
                        }

                    }
                    var flag = false;
                    for (var i = 0, len = this.cubeRotateStatus.length; i < len; i++) {
                        if (this.cubeRotateStatus[i]) {
                            flag = true;
                            break;
                        }
                    }
                    if (!flag) {//一个都没转的情况 则强制补充一个
                        var index = this.random(1, this.dimension);
                        this.rotateBox(this.angle, rotateDirect, index, getFaceFun.call(this, index)); //正向转动90
                        this.cubeRotateStatus[index - 1] = 1; //全都不转动 默认选出一个 使其正向转动指定度数
                    }
                    setTimeout(this.timerFun, 100);
                    this.rollMoveStack.push({ rotateFace: this.rotateFace, cubeRotateStatus: this.cubeRotateStatus }); //记录动作状态
                    if (this.statusCallBack != null && typeof (this.statusCallBack) == "function")
                        this.statusCallBack(this.rollMoveStack.length);
                    if (this.rollMoveStack.length == this.rollbackPoint)//判断当达到阀值时切换动作方向为回归
                    {
                        if (this.isRandomConfuse)
                            this.autoPlay = false;//如果是随机打乱模式则终止
                        else
                            this.moveDirect = false;//否则反方向回归
                    }

                }
View Code

 

这个方法将随机的产生要旋转那个轴上的那几个面以及每个面是正向还是反向旋转的控制参数,然后调用

rotateBox: function (angle, rotateDirect, faceIndex, cubes) {
                    if (cubes != null) {
                        var startStatus = rotateDirect + "(0deg)", endStatus = rotateDirect + "(" + angle + "deg)";
                        // this.changeAnimationStatus("mydhua", startStatus, endStatus)
                        for (var i = 0, len = cubes.length; i < len; i++) {
                            var ruleName = "roateRule" + faceIndex + i;
                            this.isRunning++; //组成员转动统计
                            //if (cubes[i].rotateTransfrom != "")
                            //    startStatus = cubes[i].rotateTransfrom;
                            cubes[i].rotateTransfrom = endStatus;
                            if (this.findKeyframesRule(ruleName) == null)
                                this.createKeyframesRule(ruleName, cubes[i].intalTransform + " " + startStatus, cubes[i].intalTransform + " " + endStatus);
                            else
                                this.changeAnimationStatus(ruleName, cubes[i].intalTransform + " " + startStatus, cubes[i].intalTransform + " " + endStatus);
                            cubes[i].Box.style[this.browserPrefix + "AnimationName"] = "none";
                            this.cubeMoveQueue.push({ cube: cubes[i], rule: ruleName });
                        }
                    }
                },
View Code

 

进行要旋转动画的附加,将这些要旋转的小立方体附加完动画后则放到动画队列里然后延时调用timerFun方法从队列中取出对象应用动画,此时未到动画播放完成前,对于动画播放过程进行加锁控制,防止动画的重复执行,产生混乱状况。

第五步:如何保持旋指定面的旋转状态,实现魔方旋转特效

这一步我认为是最为重要的一步,比如先绕Z轴索引为3的面也就是魔方的前面(front)正向顺时针旋转90度后,再绕X轴索引为1的面(left)正向顺时针旋转90后应该是这个样子



 

 

 

 

如何实现这种效果的?正常的逻辑是什么?是不是魔方前面的所有立方体按Z轴顺时针旋转90后,再获取魔方左面上的立方体按X轴顺时针旋转90呢?而现实却不是这样的,这样的逻辑会有两个问题;

第一个问题是:当动画播放完成后则会恢复对象播放之前的状态,对象都还是在原来的位置,这时候或许有人回想,那么既然播放完成后会恢复到原来的状态,那么在完成事件里则再按照动画旋转的方向和角度,实际旋转对应的角度和方向,那么对象不都到了旋转后的位置上了吗,对,这样想是可以的,但是当再用getXFace这方法来获取索引为1(魔方左面)上的立方体时获取其实还是左面(left)颜色都为绿色的立方体,而不包含现在新旋转过来的粉红色的立方体,所以旋转后根本不是右边图的样子。所以需要想用getXFace这个方法获取到6个绿色的立方体及3个新旋转过来的粉红色的立方体,则需要这3个粉红色的立方体跟原来位置的绿色的立方体换一下即(18,19,20跟18,21,24),这个逻辑好像也是对的,这便是第二个问题,但是换之后会形成什么样子呢?那就是再进行绕X轴索引为1的面(left)正向顺时针旋转90时就不在是绕着X轴旋转了,其实就是从上面(top)的Z轴索引为3的立方体旋转到X轴索引为1的下面立方体(bottom)即(18->6,19->21,20->24),所以两种位置的状态的动画则不再是魔方按某个面旋转的效果。所以最终的办法就是在动画结束事件里换面的颜色,代码如下:

 animationEnd: function () {
                    var offset = this.angle / 90, faceCubes = [], otherFace;
                    var zSideFace = ["top", "left", "bottom", "right"], xSideFace = ["back", "top", "front", "bottom"], ySideFace = ["back", "left", "front", "right"], sideFace = [];
                    for (var i = 0, len = this.cubeRotateStatus.length; i < len; i++) {
                        var status = this.cubeRotateStatus[i];
                        if (status) {
                            var dimensionIndex = i + 1;
                            switch (this.rotateFace) {
                                case "rotateX": faceCubes = this.getXFace(dimensionIndex); sideFace = xSideFace; if (dimensionIndex == 1) otherFace = "left"; else if (dimensionIndex == this.dimension) otherFace = "right"; break;
                                case "rotateY": faceCubes = this.getYFace(dimensionIndex); sideFace = ySideFace; if (dimensionIndex == 1) otherFace = "top"; else if (dimensionIndex == this.dimension) otherFace = "bottom"; break;
                                case "rotateZ": faceCubes = this.getZFace(dimensionIndex); sideFace = zSideFace; if (dimensionIndex == 1) otherFace = "back"; else if (dimensionIndex == this.dimension) otherFace = "front"; break;
                            }
                            this.setRotateDirectSideBackGround(faceCubes, sideFace, offset, status);
                            if (dimensionIndex == 1 || dimensionIndex == this.dimension)
                                this.setRotateOtherDirectSideBackGround(faceCubes, otherFace, offset, status);
                        }

                    }
                    //  console.info(this.rollMoveStack.length + "," + this.moveDirect);
                    if (this.autoPlay) {
                        if (this.moveDirect)
                            this.moveMagicCube();
                        else
                            this.moveRollBackCube();
                    }
                    else {
                        this.lock = false;
                    }
                    // alert("运行结束");
                }
View Code

 这个方法里有两个很重要的方法一个是

setRotateDirectSideBackGround: function (faceCubes, sideFace, offset, status) {
                    var oldSides = this.getSideCubes(faceCubes, 0), backColor = [];
                    if (oldSides == null)
                        return;
                    var offsetNIndex, offsetPIndex;
                    for (var j = 0; j < 4; j++) {
                        offsetPIndex = (j - offset + 4) % 4;
                        offsetNIndex = (j + offset) % 4;
                        var offsetIndex;
                        if (this.rotateFace == "rotateY") {
                            offsetIndex = status == 1 ? offsetPIndex : offsetNIndex;
                        }
                        else {
                            offsetIndex = status == 2 ? offsetPIndex : offsetNIndex;
                        }
                        backColor[j] = this.getSideBackGround(oldSides[offsetIndex], sideFace[offsetIndex]);
                    }
                    for (var j = 0; j < 4; j++) {
                        for (var k = 0; k < oldSides[j].length; k++) {
                            oldSides[j][k][sideFace[j]].style.background = backColor[j][k];
                        }
                    }
                }
View Code

 

他的作用是18->24,19->21,20->18;20->18,23->19,26->20;26->20,25->23,24->20;24->26,21->25->18->24 即用->前面标号的颜色代替后面标号的颜色,如上图中浅绿色前头标识,这里替换的方向是顺着旋转方向的面。

另一个是

setRotateOtherDirectSideBackGround: function (faceCubes, otherFace, offset, status) {
                    var oldSides = [], backColor = [];
                    var offsetNIndex, offsetPIndex;
                    for (var i = 0; i <= parseInt(this.dimension / 2) - 1; i++) {
                        oldSides = this.getSideCubes(faceCubes, i), backColor = [];
                        for (var j = 0; j < 4; j++) {
                            offsetPIndex = (j - offset + 4) % 4;
                            offsetNIndex = (j + offset) % 4;
                            var offsetIndex;
                            if (this.rotateFace == "rotateY") {
                                offsetIndex = status == 1 ? offsetPIndex : offsetNIndex;
                            }
                            else {
                                offsetIndex = status == 2 ? offsetPIndex : offsetNIndex;
                            }
                            backColor[j] = this.getSideBackGround(oldSides[offsetIndex], otherFace);
                        }
                        var hasManaged = [];
                        for (var j = 0; j < 4; j++) {
                            for (var k = 0; k < oldSides[j].length; k++) {
                                if (hasManaged.indexOf(oldSides[j][k].index) < 0) {
                                    oldSides[j][k][otherFace].style.background = backColor[j][k];
                                    hasManaged.push(oldSides[j][k].index);
                                }
                            }
                        }
                    }

                }
View Code

 

他的作用是 18->21,21->24;24->25,25->26;26->23,23->20;20->19,19->18;也是用->前面标号的颜色代替后面标号的颜色, 如上图中白色前头标识,不过这里是对应旋转方向两侧的面。

而实现这两个方法的基础则是

 getSideCubes: function (cubes, circleIndex) {//获取魔方某个面上的从外围开始的指定圈上的立方体,分成上下左右四个数组,上右倒叙,使整个圈上按一个顺序排列
                    var sides = [], top = [], left = [], bottom = [], right = [];
                    if (circleIndex < 0 || circleIndex > this.dimension / 2 - 1)
                        return null;
                    for (var i = 0, count = this.dimension - circleIndex * 2; i < count; i++) {
                        top.push(cubes[circleIndex * this.dimension + circleIndex + i]);
                        left.push(cubes[circleIndex * this.dimension + circleIndex + i * this.dimension]);
                        bottom.push(cubes[(this.dimension - 1 - circleIndex) * this.dimension + circleIndex + i]);
                        right.push(cubes[circleIndex * this.dimension + circleIndex + i * this.dimension + (this.dimension - (circleIndex * 2) - 1)]);
                    }
                    sides.push(this.orderByDesc(top));
                    sides.push(left);
                    sides.push(bottom);
                    sides.push(this.orderByDesc(right));
                    return sides;
                }
View Code

 

两个参数cubes:指的是某个面上的所有小立方体,circleIndex指的是从外到内的第几圈对应的所有立方体,并且全部按照顺时针排序。

好了整个原理到此已经差不多了,具体其他细节就看代码吧,完整代码如下:

   1 <!DOCTYPE html>
   2 <html>
   3 <head>
   4     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   5     <title></title>
   6     <meta charset="utf-8" />
   7     <meta name="viewport" content="target-densitydpi=320,width=640,user-scalable=no">
   8     <script language="javascript" type="text/javascript">
   9         var cache = {};
  10         (function (exports) {
  11             //手机滑动事件对象
  12             function touchEvent(target, startEvent, moveEvent, endEvent) {
  13                 var slider = {
  14                     //判断设备是否支持touch事件
  15                     touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
  16                     slider: null,
  17                     startEvent: null,
  18                     moveEvent: null,
  19                     endEvent: null,
  20                     //滑动开始
  21                     start: function (event) {
  22                         var touch = event.targetTouches[0];     //touches数组对象获得屏幕上所有的touch,取第一个touch
  23                         endPos = startPos = { x: touch.pageX, y: touch.pageY, time: +new Date };    //取第一个touch的坐标值
  24                         isScrolling = 0;   //这个参数判断是垂直滚动还是水平滚动
  25                         slider.slider.addEventListener('touchmove', slider.move, false);
  26                         slider.slider.addEventListener('touchend', slider.end, false);
  27                         if (slider.startEvent != undefined && typeof (slider.startEvent) == "function")
  28                             slider.startEvent.call(this, touch);
  29                     },
  30                     //移动
  31                     move: function (event) {
  32                         //当屏幕有多个touch或者页面被缩放过,就不执行move操作
  33                         if (event.targetTouches.length > 1 || event.scale && event.scale !== 1) return;
  34                         var touch = event.targetTouches[0];
  35                         endPos = { x: touch.pageX - startPos.x, y: touch.pageY - startPos.y };
  36                         isScrolling = Math.abs(endPos.x) < Math.abs(endPos.y) ? 1 : 0;    //isScrolling为1时,表示纵向滑动,0为横向滑动
  37                         if (isScrolling === 0) {
  38                            // event.preventDefault();      //阻止触摸事件的默认行为,即阻止滚屏
  39                         }
  40                         if (slider.moveEvent != undefined && typeof (slider.moveEvent) == "function")
  41                             slider.moveEvent.call(this, touch);
  42                     },
  43                     //滑动释放
  44                     end: function (event) {
  45                         var duration = +new Date - startPos.time;    //滑动的持续时间
  46                         if (isScrolling === 0) {    //当为水平滚动时
  47                             if (Number(duration) > 10) {
  48                                 //判断是左移还是右移,当偏移量大于10时执行
  49                                 if (endPos.x > 10) {
  50 
  51                                 } else if (endPos.x < -10) {
  52 
  53                                 }
  54                             }
  55                         }
  56                         if (slider.endEvent != undefined && typeof (slider.endEvent) == "function")
  57                             slider.endEvent.call(this, event);
  58                         //解绑事件
  59                         slider.slider.removeEventListener('touchmove', slider.move, false);
  60                         slider.slider.removeEventListener('touchend', slider.end, false);
  61                     }
  62                     ,
  63 
  64                     //初始化
  65                     init: function () {
  66                         var self = this;     //this指slider对象
  67                         if (!!self.touch) self.slider.addEventListener('touchstart', this.start, false);    //addEventListener第二个参数可以传一个对象,会调用该对象的handleEvent属性
  68                     }
  69                 }
  70                 slider.slider = target;
  71                 slider.startEvent = startEvent;
  72                 slider.moveEvent = moveEvent;
  73                 slider.endEvent = endEvent;
  74                 slider.init();
  75                 return { touch: slider.touch, target: slider.slider }
  76             };
  77             //windows按下拖动对象
  78             function dragEvent(down_target, move_target, down, move, up) {
  79                 var drag = {
  80                     down_target: null,
  81                     move_target: null,
  82                     move: null,
  83                     up: null,
  84                     down: null,
  85                     intial: function (down_target, move_target, down, move, up) {
  86                         if (down_target == undefined || move_target == undefined || move == undefined)
  87                             return;
  88                         this.down_target = down_target;
  89                         this.move_target = move_target;
  90                         this.down = down;
  91                         this.move = move;
  92                         this.up = up;
  93                         drag.prefixedEvent(drag.down_target, "mousedown", this.mousedown, true);
  94                         drag.prefixedEvent(drag.down_target, "mouseup", this.mouseup, true);
  95                         drag.prefixedEvent(document, "mouseup", this.mouseup, true);
  96                     },
  97                     mouseup: function (e) {
  98                         drag.stopEvent(e); //这里需要冒泡
  99                         if (drag.up != undefined && typeof (drag.up) == "function")
 100                             drag.up.call(this, e);
 101                         drag.prefixedEvent(drag.move_target, "mousemove", drag.mousemove, false);
 102                     },
 103                     mousemove: function (evt) {
 104                         var e = (evt) ? evt : window.event;
 105                         drag.move.call(this, e);
 106                     },
 107                     mousedown: function (evt) {
 108                         drag.stopEvent(evt); //这里阻止冒泡
 109                         var e = (evt) ? evt : window.event;
 110                         if (drag.down != undefined && typeof (drag.down) == "function")
 111                             drag.down.call(this, e);
 112                         drag.prefixedEvent(drag.move_target, "mousemove", drag.mousemove, true);
 113                     },
 114                     stopEvent: function (evt) {
 115                         if (evt == undefined) return;
 116                         var e = (evt) ? evt : window.event; //判断浏览器的类型,在基于ie内核的浏览器中的使用cancelBubble
 117                         if (window.event) { e.cancelBubble = true; } else { e.stopPropagation(); }
 118                     },
 119                     prefixedEvent: function (element, type, callback, isAdd) {
 120                         var pfx = ["webkit", "moz", "MS", "o", ""];
 121                         for (var p = 0; p < pfx.length; p++) {
 122                             if (!pfx[p]) type = type.toLowerCase();
 123                             if (isAdd)
 124                                 element.addEventListener(pfx[p] + type, callback, false);
 125                             else
 126                                 element.removeEventListener(pfx[p] + type, callback, false);
 127                         }
 128                     }
 129                 }
 130                 drag.intial(down_target, move_target, down, move, up);
 131                 return drag;
 132             }
 133 
 134             //手动旋转控制器面板
 135             function Controller(opts) {
 136                 opts = opts || {};
 137                 this.parent = opts.parent; //插入到哪里
 138                 this.pWidth = this.parent.clientWidth;
 139                 this.pHeight = this.parent.clientHeight;
 140                 this.width = this.pWidth / 3;
 141                 this.height = this.pHeight / 3;
 142                 this.upImg = opts.upImg || 'http://cdn.attach.w3cfuns.com/notes/pics/201605/30/104921ksxt7eexn5ox17oa.png';
 143                 this.downImg = opts.upImg || 'http://cdn.attach.w3cfuns.com/notes/pics/201605/30/104921hh9accx2ymfc42xb.png';
 144                 this.leftImg = opts.upImg || 'http://cdn.attach.w3cfuns.com/notes/pics/201605/30/104921wufxpjffbplehvll.png';
 145                 this.rightImg = opts.upImg || 'http://cdn.attach.w3cfuns.com/notes/pics/201605/30/104921mpvrvnjoxirj8ov7.png';
 146                 this.Click = opts.Click || null;
 147                 this.init();
 148             }
 149             Controller.prototype = {
 150                 init: function () {
 151                     this.createUp();
 152                     this.createDown();
 153                     this.createLeft();
 154                     this.createRight();
 155                 },
 156                 createDiv: function () {
 157                     var div = document.createElement('div');
 158                     div.style.margin = 0;
 159                     div.style.position = "absolute";
 160                     div.style.width = this.width + "px";
 161                     div.style.height = this.height + "px";
 162                     //div.style.backgroundRepeat = "no-repeat";
 163                     //div.style.backgroundPosition = "center";
 164                     //div.style.backgroundAttachment = "fixed";
 165                     div.style.backgroundSize = "100% 100%";
 166                     div.style.cursor = "pointer";
 167                     var that = this;
 168                     div.addEventListener("mouseup", this.Click, false);
 169                     return div;
 170                 },
 171                 createUp: function () {
 172                     var div = this.createDiv();
 173                     div.style.backgroundImage = "url(" + this.upImg + ")";
 174                     div.style.left = "33%";
 175                     div.style.top = "10%";
 176                     div.direct = "up";
 177                     this.up = div;
 178                     this.parent.appendChild(div);
 179                 },
 180                 createDown: function () {
 181                     var div = this.createDiv();
 182                     div.style.backgroundImage = "url(" + this.downImg + ")";
 183                     div.style.left = "33%";
 184                     div.style.top = "56%";
 185                     div.direct = "down";
 186                     this.down = div;
 187                     this.parent.appendChild(div);
 188                 },
 189                 createLeft: function () {
 190                     var div = this.createDiv();
 191                     div.style.backgroundImage = "url(" + this.leftImg + ")";
 192                     div.style.left = "10%";
 193                     div.style.top = "33%";
 194                     div.direct = "left";
 195                     this.left = div;
 196                     this.parent.appendChild(div);
 197                 },
 198                 createRight: function () {
 199                     var div = this.createDiv();
 200                     div.style.backgroundImage = "url(" + this.rightImg + ")";
 201                     div.style.left = "56%";
 202                     div.style.top = "33%";
 203                     div.direct = "right";
 204                     this.right = div;
 205                     this.parent.appendChild(div);
 206                 }
 207             }
 208             //单个立方体对象
 209             function Cube(opts) {
 210                 opts = opts || {};
 211                 this.parent = opts.parent; //插入到哪里
 212                 this.browserPrefix = opts.browserPrefix;
 213                 this.width = opts.width;
 214                 this.height = opts.height;
 215                 this.cubZ = opts.cubZ;
 216                 this.face = opts.face;
 217                 this.row = opts.row;
 218                 this.column = opts.column;
 219                 this.index=opts.index;
 220                 this.offsetX = this.column * (this.width + opts.marginX); //
 221                 this.offsetY = this.row * (this.height + opts.marginY); //
 222                 this.offsetZ = this.face * (this.cubZ + opts.marginZ); //
 223                 this.positiveZ = this.cubZ / 2;
 224                 this.negativeZ = -this.cubZ / 2;
 225                 this.cubFaceInfo = opts.cubFaceInfo;
 226                 this.dimension = opts.dimension;
 227                 this.centerX = (this.dimension * this.width + (this.dimension - 1) * opts.marginX) / 2;
 228                 this.centerY = (this.dimension * this.height + (this.dimension - 1) * opts.marginY) / 2;
 229                 this.centerZ = (this.dimension * this.cubZ + (this.dimension - 1) * opts.marginZ) / 2;
 230                 this.translateX = this.offsetX - this.centerX; //把中心点设为原点
 231                 this.translateY = this.offsetY - this.centerY; //
 232                 this.translateZ = this.cubZ / 2 + this.offsetZ - this.centerZ; //offsetZ按上面计算应该跟x,y在一个平面上即后面,但实际上由于要形成立方体,在Z轴上已经后退了cubZ/2个距离,故为和上面保持一致在一个面上,这里需要再加回cubZ/2个距离,使默认的xyz原点都在一个平面上即立方体后面左上角三维坐标系,以这个点作为参考点平移和设置旋转原点
 233                 this.cubeFace = [];
 234                 this.rotateTransfrom = "";
 235                 this.init();
 236 
 237             }
 238             Cube.prototype = {
 239                 init: function () {
 240                     this.createCubeBox();
 241                     this.createFront();
 242                     this.createBack();
 243                     this.createTop();
 244                     this.createBottom();
 245                     this.createLeft();
 246                     this.createRight();
 247 
 248                 },
 249                 createCubeBox: function () {
 250                     this.Box = document.createElement('div');
 251                     this.Box.style.width = this.width + "px";
 252                     this.Box.style.height = this.height + "px";
 253                     this.Box.style.left = "50%";
 254                     this.Box.style.top = "50%";
 255                     this.Box.style.position = "absolute";
 256                     this.Box.style[this.browserPrefix + "TransformStyle"] = "preserve-3d";
 257                     this.Box.style[this.browserPrefix + "Perspective"] = "0";
 258                     //                    this.Scene.style[this.browserPrefix + "backfaceVisibility"] = "hidden";
 259                     this.intalTransform = "translateZ(" + this.translateZ + "px) translateX(" + this.translateX + "px) translateY(" + this.translateY + "px)";
 260                     this.Box.style[this.browserPrefix + "Transform"] = this.intalTransform;
 261                     this.Box.style[this.browserPrefix + "TransformOrigin"] = "" + (-this.translateX) + "px " + (-this.translateY) + "px " + (-this.translateZ) + "px";
 262                     this.Box.index=this.index;
 263                     this.parent.appendChild(this.Box);
 264                     this.x = window.getComputedStyle(this.Box).getPropertyValue('left');
 265                     this.y = window.getComputedStyle(this.Box).getPropertyValue('top');
 266                     this.matrix3d = window.getComputedStyle(this.Box).getPropertyValue('transform');
 267                 },
 268                 createFace: function () {
 269                     var face = document.createElement('div');
 270                     face.style.margin = 0;
 271                     face.style.position = "absolute";
 272                     face.style.width = this.width + "px";
 273                     face.style.height = this.height + "px";
 274                     face.style.textAlign="center";
 275                     face.style.lineHeight=this.height + "px";
 276                    // face.innerHTML=this.index;
 277                     return face;
 278                 },
 279                 createFront: function () {
 280                     var face = this.createFace();
 281                     face.style[this.browserPrefix + "Transform"] = "translateZ(" + this.positiveZ + "px) ";
 282                     face.translateZ = this.positiveZ; //给生成控制面板用
 283                     this.cubeFace.push(face);
 284                     this.front = face;
 285                     this.Box.appendChild(face);
 286                 },
 287                 createBack: function () {
 288                     var face = this.createFace();
 289                     face.style[this.browserPrefix + "Transform"] = "translateZ(" + this.negativeZ + "px) ";
 290                     face.translateZ = this.negativeZ; //给生成控制面板用
 291                     this.cubeFace.push(face);
 292                     this.back = face;
 293                     this.Box.appendChild(face);
 294                 },
 295                 createTop: function () {
 296                     var face = this.createFace();
 297                     face.style[this.browserPrefix + "Transform"] = "rotateX(90deg) translateZ(" + this.positiveZ + "px) ";
 298                     face.translateZ = this.positiveZ; //给生成控制面板用
 299                     this.cubeFace.push(face);
 300                     this.top = face;
 301                     this.Box.appendChild(face);
 302                 },
 303                 createBottom: function () {
 304                     var face = this.createFace();
 305                     face.style[this.browserPrefix + "Transform"] = "rotateX(90deg) translateZ(" + this.negativeZ + "px) ";
 306                     face.translateZ = this.negativeZ; //给生成控制面板用
 307                     this.cubeFace.push(face);
 308                     this.bottom = face;
 309                     this.Box.appendChild(face);
 310                 },
 311                 createLeft: function () {
 312                     var face = this.createFace();
 313                     face.style[this.browserPrefix + "Transform"] = "rotateY(90deg) translateZ(" + this.negativeZ + "px) ";
 314                     face.translateZ = this.negativeZ; //给生成控制面板用
 315                     this.cubeFace.push(face);
 316                     this.left = face;
 317                     this.Box.appendChild(face);
 318                 },
 319                 createRight: function () {
 320                     var face = this.createFace();
 321                     face.style[this.browserPrefix + "Transform"] = "rotateY(90deg) translateZ(" + (this.positiveZ) + "px) ";
 322                     face.translateZ = this.positiveZ; //给生成控制面板用
 323                     this.cubeFace.push(face);
 324                     this.right = face;
 325                     this.Box.appendChild(face);
 326                 }
 327 
 328             }
 329             exports.magicCube = function (opts) {
 330                 opts = opts || {};
 331                 //外部传入参数
 332                 this.parent = opts.parent || document.getElementsByTagName('body')[0];
 333                 this.dimension = opts.dimension || 3;  //魔方级数
 334                 this.cubWidth = opts.cubWidth || 50;   //单个立方体宽度
 335                 this.cubHeight = opts.cubHeight || 50; //单个立方体高度
 336                 this.marginLeft = opts.marginLeft || 0; //水平方向间距
 337                 this.marginTop = opts.marginLeft || 0; //上下方向间距
 338                 this.marginZ = opts.marginZ || 0;      //前后方向间距
 339                 this.cubZ = opts.cubZ || 50;           //单个立方体Z轴距离
 340                 this.sceneWidth = opts.sceneWidth || (opts.parent != null ? opts.parent.clientWidth : null);     //3d场景宽度
 341                 this.sceneHeight = opts.sceneHeight || (opts.parent != null ? opts.parent.clientHeight : null);   //3d场景高度
 342                 this.Perspective = opts.Perspective || 0; //投影值
 343                 this.opacity = opts.opacity || 1;
 344                 this.cubFaceInfo = opts.cubFaceInfo || { front: { backGround: "rgba(255,0,0," + this.opacity + ")" }, back: { backGround: "rgba(255,255,0," + this.opacity + ")" }, left: { backGround: "rgba(0,255,0," + this.opacity + ")" }, right: { backGround: "rgba(0,0,255," + this.opacity + ")" }, top: { backGround: "rgba(255,0,255," + this.opacity + ")" }, bottom: { backGround: "rgba(255,255,255," + this.opacity + ")" }, inner: { backGround: "rgba(150,150,150," + this.opacity + ")" } }; //立方体面信息
 345                 this.angle = opts.angle || 90;         //转动的角度
 346                 this.rollbackPoint = opts.rollbackPoint || 10; //回滚的步数(自动播放时指定此步数可回归)
 347                 this.autoPlay = opts.autoPlay || false;            //是否自动播放
 348                 this.delay = opts.delay || 1;                      //动画延时
 349                 this.isRandomConfuse = opts.isRandomConfuse || false;//是否自动旋转时是随机打乱模式是,到达指定回归点后便停止自动播放
 350                 this.statusCallBack = opts.statusCallBack || null;//外部状态回调函数用于显示步数
 351                 //内部使用参数
 352                 this.faceCount = this.dimension * this.dimension; //每面立方体个数
 353                 this.count = this.dimension * this.dimension * this.dimension; //立方体总个数
 354                 this.cubes = []; //记录组成魔方的单个立方体对象
 355                 this.controllers = []; //记录控制面板对象
 356                 this.browserPrefix = "";
 357                 this.isRunning = 0;
 358                 this.timer = null;
 359                 this.rotateFace; //转动的3维坐标系方向
 360                 this.moveDirect = true; //正向随机动作还是回归,默认为正向
 361                 this.cubeMoveQueue = [];
 362                 this.rollMoveStack = []; //动作回归的堆栈
 363                 this.lock = false;         //旋转事件锁
 364                 this.scenceRotateXed = 30;
 365                 this.scenceRotateYed = -30;
 366                 this.init();
 367             };
 368             magicCube.prototype = {
 369                 init: function () {
 370                     this.start();
 371                 },
 372                 create3dScene: function () {
 373                     this.Scene = document.createElement('div');
 374                     //this.Scene.className = "cube";
 375                     var width = this.sceneWidth || this.clientWidth,
 376                         height = this.sceneHeight || this.clientHeight;
 377                     this.Scene.style.width = width + "px";
 378                     this.Scene.style.height = height + "px";
 379                     this.Scene.style.position = "relative";
 380                     this.Scene.style.top = "0";
 381                     this.Scene.style.left = "0";
 382                     this.Scene.style[this.browserPrefix + "TransformStyle"] = "preserve-3d";
 383                     this.Scene.style[this.browserPrefix + "Perspective"] = this.Perspective + "px";
 384                     //                    this.Scene.style[this.browserPrefix + "backfaceVisibility"] = "hidden";
 385                     this.Scene.style[this.browserPrefix + "Transform"] = "rotateX(" + this.scenceRotateYed + "deg) rotateY(" + this.scenceRotateXed + "deg) ";
 386                     var that = this;
 387                     function down(e) {
 388                         document.downX = e.pageX;
 389                         document.downY = e.pageY;
 390                         document.rotateX = that.scenceRotateXed;
 391                         document.rotateY = that.scenceRotateYed;
 392                     }
 393                     function move(e) {
 394                         var rotateX = e.pageX - document.downX + that.scenceRotateXed,
 395                             rotateY = document.downY - e.pageY + that.scenceRotateYed;
 396                         // console.log(rotateX + "," + rotateY);
 397                         that.Scene.style[that.browserPrefix + "Transform"] = "rotateX(" + rotateY + "deg) rotateY(" + rotateX + "deg) ";
 398                         document.rotateX = rotateX;
 399                         document.rotateY = rotateY;
 400                     }
 401                     function up(e) {
 402                         if (this == that.parent) {
 403                             that.scenceRotateXed = document.rotateX;
 404                             that.scenceRotateYed = document.rotateY;
 405                         }
 406                     }
 407                     dragEvent(this.parent, this.parent, down, move, up);
 408                     touchEvent(this.parent, down, move, up);
 409                     this.parent.appendChild(this.Scene);
 410 
 411                 },
 412                 create: function (face, row, column,index) {
 413                     return new Cube({
 414                         parent: this.Scene,
 415                         dimension: this.dimension,
 416                         width: this.cubWidth,
 417                         height: this.cubHeight,
 418                         cubZ: this.cubZ,
 419                         face: face,
 420                         row: row,
 421                         column: column,
 422                         index:index,
 423                         browserPrefix: this.browserPrefix,
 424                         cubFaceInfo: this.cubFaceInfo,
 425                         marginX: this.marginLeft,
 426                         marginY: this.marginTop,
 427                         marginZ: this.marginZ,
 428                         dimension: this.dimension
 429                     });
 430                 },
 431                 createMagicCube: function (index) {
 432                     var face = 0, row = 0, column = 0;
 433                     for (var i = 0; i < this.count; i++) {
 434                         this.cubes.push(this.create(face, row, column,i));
 435                         column++;
 436                         if ((i + 1) % this.dimension === 0) {
 437                             row++;
 438                             column = 0;
 439                         }
 440                         if ((i + 1) % this.faceCount === 0) {
 441                             face++;
 442                             row = 0;
 443                         }
 444                     }
 445                 },
 446                 drawBackGroundColor: function () {
 447                     for (var face in this.cubFaceInfo) {
 448                         if (face == "inner") {
 449                             this.setInnerBKColor(this.cubFaceInfo[face].backGround);
 450                         }
 451                         else {
 452                             var cube = this.getCubesByFace(face);
 453                             for (var i = 0, len = cube.length; i < len; i++) {
 454                                 var controlPanel = this.getControlPanel(cube[i].Box, cube[i][face]);
 455                                 this.createController(controlPanel, face, cube[i].index); //创建对应的控制面板
 456                                 cube[i][face].style.background = this.cubFaceInfo[face].backGround;
 457                                 this.bindMouseEvent(cube[i][face], face); //绑定触发和解除控制面板的事件
 458                             }
 459                         }
 460                     }
 461 
 462                 },
 463                 bindMouseEvent: function (faceElement, face) {
 464                     faceElement.face = face;
 465                     faceElement.addEventListener("mouseover", function (e) { _this = cache.magicCube; if (_this.lock || _this.autoPlay) return; _this.showController(this.parentNode.index, this.face); _this.stopEvent(e); }, false);
 466                     faceElement.addEventListener("mouseout", function (e) { _this = cache.magicCube; if (_this.lock || _this.autoPlay) return; _this.hideController(this.parentNode.index, this.face); _this.stopEvent(e); }, false);
 467                 },
 468                 createController: function (controlPanel, face, index) {
 469                     controlPanel.style.visibility = "hidden";
 470                     controlPanel.style.background = "rgba(255,255,255,.3)";
 471                     controlPanel.isFocus = false;
 472                     controlPanel.addEventListener("mouseover", function (e) { this.isFocus = true; }, false);
 473                     controlPanel.addEventListener("mouseout", function (e) { this.isFocus = false; _this = cache.magicCube; _this.hideController(this.index, this.face); }, false);
 474                     controlPanel.index = index; //记录索引 点击事件去查找对应的立方体
 475                     controlPanel.face = face;
 476                     var controller = new Controller({ parent: controlPanel, Click: this.RoateEvent }); //产生控制面板
 477                     controller.index = index;
 478                     controller.face = face;
 479                     this.controllers.push(controller);
 480                 },
 481                 showController: function (index, face) {
 482                     var controller = this.getControllerByIndex(index, face);
 483                     controller.style.visibility = "visible";
 484                 },
 485                 hideController: function (index, face, async) {
 486                     var controller = this.getControllerByIndex(index, face);
 487                     setTimeout(function () {
 488                         if (!controller.isFocus)
 489                             controller.style.visibility = "hidden";
 490                     }, 100);
 491 
 492                 },
 493                 getControllerByIndex: function (index, face) {
 494                     for (var i = 0, len = this.controllers.length; i < len; i++) {
 495                         if (this.controllers[i].index == index && this.controllers[i].face == face)
 496                             return this.controllers[i].parent;
 497                     }
 498                 },
 499                 stopEvent: function (evt) {
 500                     var e = (evt) ? evt : window.event; //判断浏览器的类型,在基于ie内核的浏览器中的使用cancelBubble
 501                     if (window.event) { e.cancelBubble = true; } else { e.stopPropagation(); }
 502                 },
 503                 getControlPanel: function (parent, source)  //克隆出一个面作为控制面板
 504                 {
 505                     var cNode = source.cloneNode(true);
 506                     var translateZ = parseInt(source.translateZ);
 507                     translateZ = translateZ > 0 ? 5 : -5;
 508                     cNode.style[this.browserPrefix + "Transform"] += "translateZ(" + translateZ + "px) ";
 509                     //cNode.style[this.browserPrefix + "BorderRadius"]=source.translateZ+"px";
 510                     parent.appendChild(cNode);
 511                     return cNode;
 512                 },
 513                 setInnerBKColor: function (color) {
 514                     for (var i = 0; i < this.count; i++) {
 515                         for (var j = 0; j < 6; j++) {
 516                             if (this.cubes[i].cubeFace[j].style.background == "") {
 517                                 this.cubes[i].cubeFace[j].style.background = color;
 518                             }
 519                         }
 520                     }
 521                 },
 522                 getZFace: function (zIndex) {
 523                     var zFace = [];
 524                     if (zIndex < 1 || zIndex > this.dimension)
 525                         return null;
 526                     for (var i = (zIndex - 1) * this.faceCount; i < zIndex * this.faceCount; i++) {
 527                         zFace.push(this.cubes[i]);
 528                     }
 529                     return zFace;
 530                 },
 531                 getXFace: function (xIndex) {
 532                     var xFace = [];
 533                     if (xIndex < 1 || xIndex > this.dimension)
 534                         return null;
 535                     for (var i = 0; i < this.count; i++) {
 536                         if (i % this.dimension == 0)
 537                             xFace.push(this.cubes[i + xIndex - 1]);
 538                     }
 539                     return xFace;
 540                 },
 541                 getYFace: function (yIndex) {
 542                     var yFace = [];
 543                     if (yIndex < 1 || yIndex > this.dimension)
 544                         return null;
 545                     for (var i = 0; i < this.count; i++) {
 546                         if (i % this.faceCount == (yIndex - 1) * this.dimension) {
 547                             for (var j = 0; j < this.dimension; j++)
 548                                 yFace.push(this.cubes[i + j]);
 549                         }
 550                     }
 551                     return yFace;
 552                 },
 553                 getSideCubes: function (cubes, circleIndex) {//获取魔方某个面上的从外围开始的指定圈上的立方体,分成上下左右四个数组,上右倒叙,使整个圈上按一个顺序排列
 554                     var sides = [], top = [], left = [], bottom = [], right = [];
 555                     if (circleIndex < 0 || circleIndex > this.dimension / 2 - 1)
 556                         return null;
 557                     for (var i = 0, count = this.dimension - circleIndex * 2; i < count; i++) {
 558                         top.push(cubes[circleIndex * this.dimension + circleIndex + i]);
 559                         left.push(cubes[circleIndex * this.dimension + circleIndex + i * this.dimension]);
 560                         bottom.push(cubes[(this.dimension - 1 - circleIndex) * this.dimension + circleIndex + i]);
 561                         right.push(cubes[circleIndex * this.dimension + circleIndex + i * this.dimension + (this.dimension - (circleIndex * 2) - 1)]);
 562                     }
 563                     sides.push(this.orderByDesc(top));
 564                     sides.push(left);
 565                     sides.push(bottom);
 566                     sides.push(this.orderByDesc(right));
 567                     return sides;
 568                 },
 569                 getCubesByFace: function (face) {
 570                     switch (face) {
 571                         case "front": return this.getZFace(this.dimension);
 572                         case "back": return this.getZFace(1);
 573                         case "left": return this.getXFace(1);
 574                         case "right": return this.getXFace(this.dimension);
 575                         case "top": return this.getYFace(1);
 576                         case "bottom": return this.getYFace(this.dimension);
 577                     }
 578                 },
 579                 moveMagicCube: function () {
 580                     if (this.cubes.length < 1) return;
 581                     //var cubes = this.getYFace(2);
 582                     //for (var i = 0, len = cubes.length; i < len; i++) {
 583                     //    cubes[i].Box.className = "rotate";
 584                     //}
 585                     //随机产生3D转动方向
 586                     this.isRunning = 0;
 587                     var direct = this.random(1, 3), rotateDirect = "", getFaceFun;
 588                     //  direct=3;
 589                     switch (direct) {
 590                         case 1: rotateDirect = "rotateX"; getFaceFun = this.getXFace; break;
 591                         case 2: rotateDirect = "rotateY"; getFaceFun = this.getYFace; break;
 592                         case 3: rotateDirect = "rotateZ"; getFaceFun = this.getZFace; break;
 593                     }
 594                     this.rotateFace = rotateDirect;
 595                     this.cubeRotateStatus = [];
 596                     for (var i = 1; i <= this.dimension; i++) {
 597                         var status = this.random(0, 2);
 598                         this.cubeRotateStatus.push(status);
 599                         switch (status) {
 600                             case 0: break; //不转动
 601                             case 1: this.rotateBox(this.angle, rotateDirect, i, getFaceFun.call(this, i)); break; //正向转动90
 602                             case 2: this.rotateBox(-this.angle, rotateDirect, i, getFaceFun.call(this, i)); break; //反向转动90
 603                         }
 604 
 605                     }
 606                     var flag = false;
 607                     for (var i = 0, len = this.cubeRotateStatus.length; i < len; i++) {
 608                         if (this.cubeRotateStatus[i]) {
 609                             flag = true;
 610                             break;
 611                         }
 612                     }
 613                     if (!flag) {//一个都没转的情况 则强制补充一个
 614                         var index = this.random(1, this.dimension);
 615                         this.rotateBox(this.angle, rotateDirect, index, getFaceFun.call(this, index)); //正向转动90
 616                         this.cubeRotateStatus[index - 1] = 1; //全都不转动 默认选出一个 使其正向转动指定度数
 617                     }
 618                     setTimeout(this.timerFun, 100);
 619                     this.rollMoveStack.push({ rotateFace: this.rotateFace, cubeRotateStatus: this.cubeRotateStatus }); //记录动作状态
 620                     if (this.statusCallBack != null && typeof (this.statusCallBack) == "function")
 621                         this.statusCallBack(this.rollMoveStack.length);
 622                     if (this.rollMoveStack.length == this.rollbackPoint)//判断当达到阀值时切换动作方向为回归
 623                     {
 624                         if (this.isRandomConfuse)
 625                             this.autoPlay = false;//如果是随机打乱模式则终止
 626                         else
 627                             this.moveDirect = false;//否则反方向回归
 628                     }
 629 
 630                 },
 631                 getXYByFaceCubes: function (faceCubes, index) {//获取当前当前索引对应的立方体所在面的行X列Y坐标
 632                     var y = 1, x = 1;
 633                     for (var i = 1, len = faceCubes.length; i <= len; i++) {
 634                         if (faceCubes[i - 1].index == index)
 635                             return { Y: y, X: x };
 636                         x++;
 637                         if (i % this.dimension == 0) {
 638                             y++;
 639                             x = 1;
 640                         }
 641 
 642                     }
 643                 },
 644                 RoateEvent: function (e) {
 645                     var _this = cache.magicCube, positon, rotateTarget = {};
 646                     //隐藏控制面板
 647                     //this.parentNode.isFocus = false;
 648                     //_this.hideController(this.parentNode.index, this.parentNode.face);
 649                     //开始定位立方体
 650                     var cubes = _this.getCubesByFace(this.parentNode.face);
 651                     switch (this.parentNode.face) {
 652                         case "back":
 653                         case "front": positon = _this.getXYByFaceCubes(cubes, this.parentNode.index); rotateTarget.moveY = { rotateDirect: "rotateX", getFaceFun: _this.getXFace, index: positon.X }; rotateTarget.moveX = { rotateDirect: "rotateY", getFaceFun: _this.getYFace, index: positon.Y }; break;
 654                         case "top":
 655                         case "bottom": positon = _this.getXYByFaceCubes(cubes, this.parentNode.index); rotateTarget.moveY = { rotateDirect: "rotateX", getFaceFun: _this.getXFace, index: positon.X }; rotateTarget.moveX = { rotateDirect: "rotateZ", getFaceFun: _this.getZFace, index: positon.Y }; break;
 656                         case "left":
 657                         case "right": positon = _this.getXYByFaceCubes(cubes, this.parentNode.index); rotateTarget.moveY = { rotateDirect: "rotateZ", getFaceFun: _this.getZFace, index: positon.Y }; rotateTarget.moveX = { rotateDirect: "rotateY", getFaceFun: _this.getYFace, index: positon.X }; break;
 658                     }
 659                     //rotateTarget.moveY 表示沿相对于屏幕的纵向Y方向旋转,moveX表示沿相对于屏幕的横向X方向旋转
 660                     _this.cubeRotateStatus = [];
 661                     for (var i = 1; i <= _this.dimension; i++)
 662                         _this.cubeRotateStatus[i - 1] = 0;
 663                     _this.isRunning = 0;
 664                     var angle, status;
 665                     if (this.direct == "left" || this.direct == "right") {//横向旋转
 666                         var leftOrright = this.parentNode.face == "left" || this.parentNode.face == "right"; //对于绕Y轴旋转的有点特殊跟正常方向相反
 667                         if (this.direct == "left") {
 668                             angle = leftOrright ? _this.angle : -_this.angle;
 669                             status = leftOrright ? 1 : 2;
 670                         } else {
 671                             angle = leftOrright ? -_this.angle : _this.angle;
 672                             status = leftOrright ? 2 : 1;
 673                         }
 674                         if (this.parentNode.face == "right" || this.parentNode.face == "back" || this.parentNode.face == "bottom") {//取反
 675                             angle = -angle;
 676                             status = 3 - status;
 677                         }
 678                         _this.rotateBox(angle, rotateTarget.moveX.rotateDirect, rotateTarget.moveX.index, rotateTarget.moveX.getFaceFun.call(_this, rotateTarget.moveX.index));
 679                         _this.rotateFace = rotateTarget.moveX.rotateDirect;
 680                         _this.cubeRotateStatus[rotateTarget.moveX.index - 1] = status;
 681                        
 682                     }
 683                     else if (this.direct == "up" || this.direct == "down") {//纵向旋转
 684                         if (this.direct == "up") {
 685                             angle = _this.angle
 686                             status = 1;
 687                         } else {
 688                             angle = -_this.angle;
 689                             status = 2;
 690                         }
 691                         if (this.parentNode.face == "right" || this.parentNode.face == "back" || this.parentNode.face == "bottom") {//取反
 692                             angle = -angle;
 693                             status = 3 - status;
 694                         }
 695                         _this.rotateBox(angle, rotateTarget.moveY.rotateDirect, rotateTarget.moveY.index, rotateTarget.moveY.getFaceFun.call(_this, rotateTarget.moveY.index));
 696                         _this.rotateFace = rotateTarget.moveY.rotateDirect;
 697                         _this.cubeRotateStatus[rotateTarget.moveY.index - 1] = status;
 698                     }
 699                     _this.lock = true;
 700                     setTimeout(_this.timerFun, 100);
 701                     _this.rollMoveStack.push({ rotateFace: _this.rotateFace, cubeRotateStatus: _this.cubeRotateStatus }); //记录动作状态
 702                     if (_this.statusCallBack != null && typeof (_this.statusCallBack) == "function")
 703                         _this.statusCallBack(_this.rollMoveStack.length);
 704                 },
 705                 moveRollBackCube: function () {
 706                     var record = this.rollMoveStack.pop(), getFaceFun;
 707                     this.rotateFace = record.rotateFace;
 708                     this.isRunning = 0;
 709                     switch (record.rotateFace) {
 710                         case "rotateX": getFaceFun = this.getXFace; break;
 711                         case "rotateY": getFaceFun = this.getYFace; break;
 712                         case "rotateZ": getFaceFun = this.getZFace; break;
 713                     }
 714                     this.cubeRotateStatus = [];
 715                     for (var i = 0, len = record.cubeRotateStatus.length; i < len; i++) {
 716                         var dimensionIndex = i + 1, status = record.cubeRotateStatus[i];
 717                         if (status == 1) {
 718                             this.cubeRotateStatus.push(2); //1 变2,2变1
 719                             this.rotateBox(-this.angle, record.rotateFace, dimensionIndex, getFaceFun.call(this, dimensionIndex)); //反向转动90
 720                         }
 721                         else if (status == 2) {
 722                             this.cubeRotateStatus.push(1); //1 变2,2变1
 723                             this.rotateBox(this.angle, record.rotateFace, dimensionIndex, getFaceFun.call(this, dimensionIndex)); //反向转动90
 724                         }
 725                         else {
 726                             this.cubeRotateStatus.push(0);
 727                         }
 728                     }
 729                     setTimeout(this.timerFun, 100);
 730                     if (this.statusCallBack != null && typeof (this.statusCallBack) == "function")
 731                         this.statusCallBack(this.rollMoveStack.length);
 732                     if (this.rollMoveStack.length == 0)//判断当达到0时切换动作为正向随机
 733                         this.moveDirect = true;
 734                 },
 735                 intersect: function (source, target) {
 736                     var data = [];
 737                     for (var i = 0, len = source.length; i < len; i++) {
 738                         var index = target.indexOf(source[i]);
 739                         if (index >= 0)
 740                             data.push(source[i])
 741                     }
 742                     return data;
 743                 },
 744                 orderByDesc: function (datas) {
 745                     var temp;
 746                     for (var i = 0; i < datas.length - 1; i++) {
 747                         for (var j = i + 1; j < datas.length; j++) {
 748                             if (parseFloat(datas[i].index) < parseFloat(datas[j].index)) {
 749                                 temp = datas[i];
 750                                 datas[i] = datas[j];
 751                                 datas[j] = temp;
 752                             }
 753                         }
 754                     }
 755                     return datas;
 756                 },
 757                 getSideBackGround: function (sideFaces, face) {
 758                     var backGrounds = [];
 759                     for (var i = 0, len = sideFaces.length; i < len; i++) {
 760                         backGrounds.push(sideFaces[i][face].style.background);
 761                     }
 762                     return backGrounds;
 763                 },
 764                 setRotateDirectSideBackGround: function (faceCubes, sideFace, offset, status) {
 765                     var oldSides = this.getSideCubes(faceCubes, 0), backColor = [];
 766                     if (oldSides == null)
 767                         return;
 768                     var offsetNIndex, offsetPIndex;
 769                     for (var j = 0; j < 4; j++) {
 770                         offsetPIndex = (j - offset + 4) % 4;
 771                         offsetNIndex = (j + offset) % 4;
 772                         var offsetIndex;
 773                         if (this.rotateFace == "rotateY") {
 774                             offsetIndex = status == 1 ? offsetPIndex : offsetNIndex;
 775                         }
 776                         else {
 777                             offsetIndex = status == 2 ? offsetPIndex : offsetNIndex;
 778                         }
 779                         backColor[j] = this.getSideBackGround(oldSides[offsetIndex], sideFace[offsetIndex]);
 780                     }
 781                     for (var j = 0; j < 4; j++) {
 782                         for (var k = 0; k < oldSides[j].length; k++) {
 783                             oldSides[j][k][sideFace[j]].style.background = backColor[j][k];
 784                         }
 785                     }
 786                 },
 787                 setRotateOtherDirectSideBackGround: function (faceCubes, otherFace, offset, status) {
 788                     var oldSides = [], backColor = [];
 789                     var offsetNIndex, offsetPIndex;
 790                     for (var i = 0; i <= parseInt(this.dimension / 2) - 1; i++) {
 791                         oldSides = this.getSideCubes(faceCubes, i), backColor = [];
 792                         for (var j = 0; j < 4; j++) {
 793                             offsetPIndex = (j - offset + 4) % 4;
 794                             offsetNIndex = (j + offset) % 4;
 795                             var offsetIndex;
 796                             if (this.rotateFace == "rotateY") {
 797                                 offsetIndex = status == 1 ? offsetPIndex : offsetNIndex;
 798                             }
 799                             else {
 800                                 offsetIndex = status == 2 ? offsetPIndex : offsetNIndex;
 801                             }
 802                             backColor[j] = this.getSideBackGround(oldSides[offsetIndex], otherFace);
 803                         }
 804                         var hasManaged = [];
 805                         for (var j = 0; j < 4; j++) {
 806                             for (var k = 0; k < oldSides[j].length; k++) {
 807                                 if (hasManaged.indexOf(oldSides[j][k].index) < 0) {
 808                                     oldSides[j][k][otherFace].style.background = backColor[j][k];
 809                                     hasManaged.push(oldSides[j][k].index);
 810                                 }
 811                             }
 812                         }
 813                     }
 814 
 815                 },
 816                 animationEnd: function () {
 817                     var offset = this.angle / 90, faceCubes = [], otherFace;
 818                     var zSideFace = ["top", "left", "bottom", "right"], xSideFace = ["back", "top", "front", "bottom"], ySideFace = ["back", "left", "front", "right"], sideFace = [];
 819                     for (var i = 0, len = this.cubeRotateStatus.length; i < len; i++) {
 820                         var status = this.cubeRotateStatus[i];
 821                         if (status) {
 822                             var dimensionIndex = i + 1;
 823                             switch (this.rotateFace) {
 824                                 case "rotateX": faceCubes = this.getXFace(dimensionIndex); sideFace = xSideFace; if (dimensionIndex == 1) otherFace = "left"; else if (dimensionIndex == this.dimension) otherFace = "right"; break;
 825                                 case "rotateY": faceCubes = this.getYFace(dimensionIndex); sideFace = ySideFace; if (dimensionIndex == 1) otherFace = "top"; else if (dimensionIndex == this.dimension) otherFace = "bottom"; break;
 826                                 case "rotateZ": faceCubes = this.getZFace(dimensionIndex); sideFace = zSideFace; if (dimensionIndex == 1) otherFace = "back"; else if (dimensionIndex == this.dimension) otherFace = "front"; break;
 827                             }
 828                             this.setRotateDirectSideBackGround(faceCubes, sideFace, offset, status);
 829                             if (dimensionIndex == 1 || dimensionIndex == this.dimension)
 830                                 this.setRotateOtherDirectSideBackGround(faceCubes, otherFace, offset, status);
 831                         }
 832 
 833                     }
 834                     //  console.info(this.rollMoveStack.length + "," + this.moveDirect);
 835                     if (this.autoPlay) {
 836                         if (this.moveDirect)
 837                             this.moveMagicCube();
 838                         else
 839                             this.moveRollBackCube();
 840                     }
 841                     else {
 842                         this.lock = false;
 843                     }
 844                     // alert("运行结束");
 845                 },
 846                 bindAnimationEvent: function () {
 847                     var loopMove = function () {
 848                         cache.magicCube.isRunning--; //由于按组转动,顾要等组成员都完成再进行新的动画
 849                         if (cache.magicCube.isRunning == 0)
 850                             cache.magicCube.animationEnd();
 851                     }
 852                     for (var i = 0; i < this.count; i++) {
 853 
 854                         this.prefixedEvent(this.cubes[i].Box, "AnimationEnd", loopMove, true);
 855                     }
 856                     cache.magicCube = this; //缓存,避免内存泄露
 857                 },
 858                 rotateBox: function (angle, rotateDirect, faceIndex, cubes) {
 859                     if (cubes != null) {
 860                         var startStatus = rotateDirect + "(0deg)", endStatus = rotateDirect + "(" + angle + "deg)";
 861                         // this.changeAnimationStatus("mydhua", startStatus, endStatus)
 862                         for (var i = 0, len = cubes.length; i < len; i++) {
 863                             var ruleName = "roateRule" + faceIndex + i;
 864                             this.isRunning++; //组成员转动统计
 865                             //if (cubes[i].rotateTransfrom != "")
 866                             //    startStatus = cubes[i].rotateTransfrom;
 867                             cubes[i].rotateTransfrom = endStatus;
 868                             if (this.findKeyframesRule(ruleName) == null)
 869                                 this.createKeyframesRule(ruleName, cubes[i].intalTransform + " " + startStatus, cubes[i].intalTransform + " " + endStatus);
 870                             else
 871                                 this.changeAnimationStatus(ruleName, cubes[i].intalTransform + " " + startStatus, cubes[i].intalTransform + " " + endStatus);
 872                             cubes[i].Box.style[this.browserPrefix + "AnimationName"] = "none";
 873                             this.cubeMoveQueue.push({ cube: cubes[i], rule: ruleName });
 874                         }
 875                     }
 876                 },
 877                 findKeyframesRule: function (rule) {
 878                     var ruleName = this.browserPrefix == "" ? "KEYFRAMES_RULE" : this.browserPrefix.toUpperCase() + "_KEYFRAMES_RULE";
 879                     var ss = document.styleSheets;
 880                     for (var i = 0; i < ss.length; ++i) {
 881                         for (var j = 0; j < ss[i].cssRules.length; ++j) {
 882                             if (ss[i].cssRules[j].type == window.CSSRule[ruleName] && ss[i].cssRules[j].name == rule) { return ss[i].cssRules[j]; }
 883                         }
 884                     }
 885                     return null;
 886                 },
 887                 createKeyframesRule: function (rule, startStatus, endStatus) {
 888                     var prefix = this.browserPrefix == "" ? "" : "-" + this.browserPrefix + "-";
 889                     var sheet;
 890                     if (document.styleSheets.length < 1)
 891                         sheet = this.createSheets();
 892                     else
 893                         sheet = document.styleSheets[0];
 894                     var selectorText = "@" + prefix + "keyframes " + rule;
 895                     var cssText = "0% { " + prefix + "transform: " + startStatus + "; } 100% { " + prefix + "transform: " + endStatus + "; }"
 896                     if (sheet.insertRule) {
 897                         sheet.insertRule(selectorText + "{" + cssText + "}", 0);
 898                     } else if (sheet.addRule) {//兼容IE
 899                         sheet.addRule(selectorText, cssText, 0);
 900                     }
 901                 },
 902                 removeKeyframeRule: function (keyframes) {
 903                     var length = keyframes.cssRules.length;
 904                     var keyframeString = [];
 905                     for (var i = 0; i < length; i++) {
 906                         keyframeString.push(keyframes.cssRules[i].keyText);
 907                     }
 908                     //移除动画帧规则
 909                     for (var i = 0, j = length; i < j; i++) {
 910                         if (this.browserPrefix == "webkit" || this.browserPrefix == "Moz")
 911                             keyframes.deleteRule(keyframeString[i]);
 912                         else
 913                             keyframes.deleteRule(i); //兼容IE
 914                     }
 915                 },
 916                 changeAnimationStatus: function (animationName, startStatus, endStatus) {
 917                     var keyframes = this.findKeyframesRule(animationName);
 918                     this.removeKeyframeRule(keyframes);
 919                     //重新设置帧规则
 920                     var prefix = this.browserPrefix == "" ? "" : "-" + this.browserPrefix + "-";
 921                     keyframes.appendRule("0% { " + prefix + "transform: " + startStatus + "; }");
 922                     keyframes.appendRule("100% { " + prefix + "transform: " + endStatus + "; }");
 923                 },
 924                 createSheets: function () {
 925                     // 创建 <style> 标签
 926                     var style = document.createElement("style");
 927                     // 可以添加一个媒体(/媒体查询,media query)属性
 928                     // style.setAttribute("media", "screen")
 929                     // style.setAttribute("media", "only screen and (max-width : 1024px)")
 930                     // 对WebKit hack :(
 931                     style.appendChild(document.createTextNode(""));
 932                     // 将 <style> 元素加到页面中
 933                     document.head.appendChild(style);
 934                     return style.sheet;
 935                 },
 936                 prefixedEvent: function (element, type, callback, isAdd) {
 937                     var pfx = ["webkit", "moz", "MS", "o", ""];
 938                     for (var p = 0; p < pfx.length; p++) {
 939                         if (!pfx[p]) type = type.toLowerCase();
 940                         if (isAdd)
 941                             element.addEventListener(pfx[p] + type, callback, false);
 942                         else
 943                             element.removeEventListener(pfx[p] + type, callback, false);
 944                     }
 945                 },
 946                 start: function () {
 947                     this.css();
 948                     this.prefix();
 949                     this.create3dScene();
 950                     this.createMagicCube();
 951                     this.drawBackGroundColor();
 952                     this.bindAnimationEvent(); //绑定动画播放完成事件
 953                     if (this.autoPlay)
 954                         this.moveMagicCube();     //立即开始动画
 955                     // this.timer = setInterval(this.timerFun, 100);
 956                 },
 957                 timerFun: function () {
 958                     var _this = cache.magicCube;
 959                     if (_this.isRunning >= _this.dimension) {
 960                         for (var i = 0, len = _this.cubeMoveQueue.length; i < len; i++) {
 961                             var animation = _this.cubeMoveQueue.shift();
 962                             animation.cube.Box.style[_this.browserPrefix + "Animation"] = animation.rule + " " + _this.delay + "s linear 1"; // Chrome, Safari 和 Opera 代码
 963                         }
 964                     }
 965                 },
 966                 css: function () {
 967                     var d = document,
 968                         doc = d.documentElement,
 969                         body = d.body;
 970                     this.clientWidth = doc.clientWidth;
 971                     this.clientHeight = doc.clientHeight;
 972                     if (d.compatMode != "CSS1Compat") {
 973                         this.clientWidth = body.clientWidth;
 974                         this.clientHeight = body.clientHeight;
 975                     }
 976                     // console.log(this.width +'////'+ this.height)
 977                 },
 978                 random: function (min, max) {
 979                     return (Math.random() * (max - min + 1) + min) >> 0;
 980                 },
 981                 prefix: function () {
 982                     var N = navigator.appName, ua = navigator.userAgent, tem;
 983                     var M = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
 984                     if (M && (tem = ua.match(/version\/([\.\d]+)/i)) != null) M[2] = tem[1];
 985                     M = M ? [M[1], M[2]] : [N, navigator.appVersion, '-?'];
 986                     M = M[0];
 987                     if (M == "Chrome") { this.browserPrefix = "webkit"; }
 988                     if (M == "Firefox") { this.browserPrefix = "Moz"; }
 989                     if (M == "Safari") { this.browserPrefix = "webkit"; }
 990                     if (M == "MSIE") { this.browserPrefix = "ms"; }
 991                 }
 992 
 993             };
 994         }(window));
 995         //自定义简单筛选器
 996         (function () {
 997             var window = this,
 998             selector = window.$ = function (sign, type) {
 999                 return new extend.init(sign, type);
1000             };
1001             extend = $.prototype = {
1002                 init: function (sign, type) {
1003                     this.targets = [];
1004                     switch (type) {
1005                         case "id": this.targets.push(document.getElementById(sign)); break;
1006                         case "name": this.targets = document.getElementsByName(sign); break;
1007                         case "class": this.targets = document.getElementsByClassName(sign); break;
1008                         case "tag": this.targets = document.getElementsByTagName(sign); break;
1009                     }
1010                     this.each(function (item, i) {
1011                         this[i] = item;
1012                     })
1013                     return this;
1014                 },
1015                 val: function (value) {
1016                     if (value == undefined)
1017                         return this.targets[0].value;
1018                     else if (this.targets != null)
1019                         this.each(function (item) {
1020                             if (item.tagName== "INPUT") {
1021                                 item.value = value;
1022                             }
1023                         });
1024                 },
1025                 Number: function () {
1026                     return parseInt(this.val())
1027                 },
1028                 each: function (fun) {
1029                     if (fun != undefined && typeof (fun) == "function") {
1030                         for (var i = 0, len = this.targets.length; i < len; i++) {
1031                             fun.call(this, this.targets[i], i)
1032                         }
1033                     }
1034                 },
1035                 setClass: function (className) {
1036                     if (this.targets != null)
1037                         this.each(function (item) {
1038                             item.className = className;
1039                         });
1040                 },
1041                 addClass: function (className) {
1042                     if (this.targets != null)
1043                         this.each(function (item) {
1044                             item.classList.add(className);
1045                         });
1046                 },
1047                 removeClass: function (className) {
1048                     if (this.targets != null)
1049                         this.each(function (item) {
1050                             item.classList.remove(className);
1051                         });
1052                 }
1053             };
1054             extend.init.prototype = extend;//实现了可扩展性
1055         })();
1056     </script>
1057 </head>
1058 <body style="background-color: black">
1059     <style type="text/css">
1060         body
1061         {
1062             margin: 0;
1063             padding: 5px;
1064             font-family: 微软雅黑;
1065             font-size: 14px;
1066         }
1067         
1068         .head
1069         {
1070             border: 1px solid #333;
1071             background-color: #222;
1072             color: #aaa;
1073             position: relative;
1074             overflow: hidden;
1075             padding-bottom: 5px;
1076             top: 0;
1077         }
1078         
1079         #container
1080         {
1081             position: relative;
1082             height: 600px;
1083             margin: 0 auto;
1084             overflow: auto;
1085             padding: 0;
1086             top: 0;
1087             clear: both;
1088             background-color: #111;
1089         }
1090         
1091         .head .bind
1092         {
1093             float: left;
1094             width: 163px;
1095             margin-top: 5px;
1096             border-right-color: #333;
1097             border-right-style: solid;
1098             border-right-width: 1px;
1099         }
1100         .head .bind .status
1101         {
1102             border: none;
1103         }
1104         .head .bind .left
1105         {
1106             text-align: right;
1107             float: left;
1108             width: 87px;
1109             overflow: hidden;
1110             padding-right: 3px;
1111         }
1112         
1113         .head .bind .right
1114         {
1115             text-align: left;
1116             float: left;
1117             width: 68px;
1118             padding: 0;
1119             padding: 0 5px 0 0;
1120         }
1121         
1122         .head .bind .right input
1123         {
1124             width: 40px;
1125             text-align: center;
1126             background-color: #bbb;
1127             margin-right: 3px;
1128             border: 1px solid #dedede;
1129         }
1130         
1131         .head .bind .button
1132         {
1133             text-decoration: none;
1134             background-color: #005588;
1135             color: #bbf;
1136             border: 1px solid #0066ff;
1137             padding: 1px 12px 1px 12px;
1138             margin-right: 2px;
1139         }
1140         
1141         .head .bind .disbutton
1142         {
1143             text-decoration: none;
1144             background-color: #555;
1145             color: #bbb;
1146             border: 1px solid #777;
1147             padding: 1px 12px 1px 12px;
1148             margin-right: 2px;
1149         }
1150         
1151         .head .bind .button:hover
1152         {
1153             background: #0088aa;
1154             color: #eee;
1155         }
1156     </style>
1157     <div id="head" class="head">
1158         <div class="bind">
1159             <div class="left">
1160                 魔方阶数:
1161             </div>
1162             <div class="right">
1163                 <input id="dimension" type="text" value="3" />
1164             </div>
1165         </div>
1166         <div class="bind">
1167             <div class="left">
1168                 立方体宽度:
1169             </div>
1170             <div class="right">
1171                 <input id="width" type="text" value="100" />px
1172             </div>
1173         </div>
1174         <div class="bind">
1175             <div class="left">
1176                 立方体间隔:
1177             </div>
1178             <div class="right">
1179                 <input id="margin" type="text" value="10" />px
1180             </div>
1181         </div>
1182         <div class="bind">
1183             <div class="left">
1184                 透明度:
1185             </div>
1186             <div class="right">
1187                 <input id="opacity" type="text" value="90" />%
1188             </div>
1189         </div>
1190         <div class="bind">
1191             <div class="left">
1192                 打乱步数:
1193             </div>
1194             <div class="right">
1195                 <input id="rollbackPoint" type="text" value="10" />
1196             </div>
1197         </div>
1198         <div class="bind">
1199             <input id="autoPlay" type="radio" value="0" />自动播放<input id="operater" type="radio"
1200                 value="1" />手动操作
1201         </div>
1202         <div class="bind" style="width: 254px">
1203             <a class="button" id="reset" href="javascript:void(0)">重 置</a> <a class="button"
1204                 id="random" href="javascript:void(0)">随机打乱</a> <a class="button" id="back" href="javascript:void(0)">
1205                     返回上一步</a>
1206         </div>
1207         <div class="bind" style="border: none;">
1208             <div class="left">
1209                 已走步数:
1210             </div>
1211             <div class="right">
1212                 <input id="steps" readonly type="text" value="0" />
1213             </div>
1214         </div>
1215     </div>
1216     <div id="container" title="可按下鼠标移动来旋转魔方">
1217     </div>
1218     <script>
1219         var cube = null;
1220         var autoPlay = true;
1221         var container = $("container", "id")[0];
1222         var screenHeight = document.compatMode != "CSS1Compat" ? document.body.clientHeight : document.documentElement.clientHeight;
1223         var Height = screenHeight - document.getElementById("head").offsetHeight - 10;
1224         container.style.height = Height + "px";
1225         var inputs = $("input", "tag");
1226         //输入检查事件
1227         function inputBlur(e) {
1228             var patten = new RegExp(/^[0-9]*[0-9][0-9]*$/);
1229             if (!patten.test(this.value))
1230             { this.value = ""; return }
1231             switch (this.id) {
1232                 case "dimension": if (this.value < 1 || this.value > 10) { this.value = ""; return } break;
1233                 case "width": if (this.value < 10 || this.value > 1000) { this.value = ""; return } break;
1234                 case "margin": if (this.value > 1000) { this.value = ""; return } break;
1235                 case "opacity": if (this.value > 100) { this.value = ""; return } break;
1236                 case "rollbackPoint": if (this.value > 100) { this.value = ""; return } break;
1237 
1238             }
1239             build();
1240         }
1241         //自动手动事件
1242         function radioChange(select) {
1243             for (var i = 0; i < radios.length; i++) {
1244                 if (radios[i] != select) {
1245                     radios[i].checked = false;
1246                 }
1247             }
1248             if (select.value == "0") {
1249                 $("a", "tag").setClass("disbutton");
1250                 $("reset", "id")[0].removeEventListener("click", resetCube, false);
1251                 $("random", "id")[0].removeEventListener("click", randomConfuse, false);
1252                 $("back", "id")[0].removeEventListener("click", backLastStep, false);
1253                 autoPlay = true;
1254             }
1255             else {
1256                 $("a", "tag").setClass("button");
1257                 $("reset", "id")[0].addEventListener("click", resetCube, false);
1258                 $("random", "id")[0].addEventListener("click", randomConfuse, false);
1259                 $("back", "id")[0].addEventListener("click", backLastStep, false);
1260                 autoPlay = false;
1261             }
1262             if (cube != null) {
1263                 cube.autoPlay = autoPlay;
1264                 cube.delay = autoPlay ? 2 : 0.5;
1265                 if (autoPlay) {
1266                     cube.moveMagicCube();
1267                     cube.isRandomConfuse = false;
1268                 }
1269             }
1270 
1271         }
1272         //重置事件
1273         function resetCube(e) {
1274             autoPlay = false;
1275             build();
1276         }
1277         //随机打乱事件
1278         function randomConfuse(e) {
1279             if (cube != null) {
1280                 cube.isRandomConfuse = true;
1281                 cube.autoPlay = true;
1282                 cube.delay = 0.5;
1283                 cube.rollbackPoint = $("rollbackPoint", "id").Number();
1284                 cube.rollMoveStack = [];
1285                 cube.moveMagicCube();
1286             }
1287         }
1288         //返回上一步事件
1289         function backLastStep(e) {
1290             if (cube != null) {
1291                 cube.moveRollBackCube();
1292             }
1293         }
1294         var radios = [];
1295         inputs.each(function (item) {
1296             if (item.type == "text")
1297                 item.addEventListener("change", inputBlur, false);
1298             else if (item.type == "radio") {
1299                 item.addEventListener("change", function (e) { radioChange(this); }, false);
1300                 radios.push(item);
1301             }
1302         });
1303         var steps_input = $("steps", "id");
1304         function statusCallBack(steps) {
1305             steps_input.val(steps);
1306         }
1307         function build() {
1308             if (cube != null)
1309                 container.removeChild(cube.Scene);
1310             var radio;
1311             cube = new magicCube({ parent: container, dimension: $("dimension", "id").Number(), cubWidth: $("width", "id").Number(), cubHeight: $("width", "id").Number(), marginLeft: $("margin", "id").Number(), marginTop: $("margin", "id").Number(), marginZ: $("margin", "id").Number(), cubZ: $("width", "id").Number(), opacity: $("opacity", "id").Number() / 100, delay: autoPlay ? 2 : 0.5, autoPlay: autoPlay, rollbackPoint: $("rollbackPoint", "id").Number(), statusCallBack: statusCallBack });
1312             steps_input.val(0);
1313 
1314         }
1315         if (autoPlay) {
1316             radio = $("autoPlay", "id")[0];
1317             radio.checked = true;
1318         }
1319         else {
1320             radio = $("operater", "id")[0]
1321             radio.checked = true;
1322         }
1323         radioChange(radio);
1324         build();
1325     </script>
1326 </body>
1327 </html>
View Code

 

在线效果请点击这里

posted on 2016-05-30 10:38  linbl  阅读(2575)  评论(1编辑  收藏  举报