最近大家都在用代码写圣诞树,我也跟个风吧!
主要技术:
1.CSS3的3D变换
2.DOM动态添加节点
开发环境:vscode
目录
一.引入
二、基本知识
1.CSS3的3D变换
(1).空间直角坐标系
(2).transform详解
(3).transform-style
2.DOM节点操作
三、 实现
1.HTML
2.CSS
3.js
4.成果
一.引入
我们是来写圣诞树的,首先必须弄到一张设计稿:
这里,我第一时间就想到了MC的云杉,因为它和圣诞树长得很像(误):这里介绍了云杉树的详细结构
树叶贴图:
树干贴图:
二、基本知识
1.CSS3的3D变换
(1).空间直角坐标系
CSS3的空间直角坐标系如图,和数学上的不太一样:y轴正方向是竖直向下的。
(2).transform详解
transform是CSS3的新特性,它可以接受的参数有:translate,rotate,scale等。多个参数之间用空格隔开。
translate可以沿坐标轴平移元素且会保留原来所占的位置(类似相对定位),分为三种,分别为translateX(),translateY(),translateZ()。它们都只接受一个参数,参数必须写明单位。
这三种translate可以简化为translate3d()。以下两种写法是等价的:
.d1{transform: translateX(200px) translateY(300px) translateZ(100px);
}.d2{transform: translate3d(200px,300px,100px);
}
translateZ()只有设置了perspective属性才起作用,它主要用于产生一种“近大远小”的感觉。
perspective表示视距,即观察者和屏幕的距离。而translateZ()表示物体到屏幕的距离。人的视线经过物体的边界,投射到屏幕上,形成我们所看到的像。
以下是解释透视现象的经典图片:这里的d就是perspective,z就是translateZ()。
rotate也分为三种:rotateX(),rotateY(),rotateZ()。它们也只接收一个参数,常用单位为deg(°)。还有一种rotate(),它与rotateZ()等价,即在二维平面上进行旋转。
不要使用rotate3d(),它使用前三个参数构造一个空间向量,再以这个向量为轴,按照第四个参数进行旋转。如下:
.d3 {transform: rotate3d(1,0,0,45deg);/*绕X轴旋转+45度*/
}
3D旋转总会碰到一个问题:角度到底是正是负?有一个“左手定则”可以解决这个问题。(大家凑合着这张图看看吧)左手握住坐标轴,大拇指指向坐标轴正方向,四指弯曲方向就是旋转角的正方向。
当我们在一个transform中同时写translate和rotate属性时,建议的写法是先写translate,再写rotate,因为rotate过程中会改变坐标轴的指向,导致translate不能按预期的那样平移。
(3).transform-style
这个属性作用于父元素上,需要手动设置为preserve-3d。否则,在父元素作3D变换的过程中,子元素就会失去3D效果。
2.DOM节点操作
- 创建节点:document.createElement('元素类型名');
var div=document.createElement('div');
- 返回子节点组成的数组:父节点.children
var spans = div.children; console.log(spans[0]);//<span>aaa<span> console.log(spans[1]);//<span>bbb<span>
spans实际上是一个伪数组,它具有length属性。
- 在父节点尾部添加节点:父节点.appendChild(子节点);
var ul = document.querySelector('ul'); var li = document.createElement('li'); ul.appendChild(li);
三、 实现
1.HTML
由于使用了动态创建节点,所以,除了引入css和js之外,无需写任何其他代码~
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="style.css"><script src="tree.js"></script>
</head><body></body></html>
2.CSS
透视perspective定义在body中,数值越大,视角越小;
定义了一个发光动画light,用于dec的闪烁效果;
三种方块:完整大小(64*64*64)的leaves和wood,不完整(64*64*8)的snow,最小的(8*8*8)的dec;
六个面的绘制使用3d变换完成;
定义了“层”layer,因为需要分8层绘制。
* {padding: 0;margin: 0;
}@keyframes light {0% {box-shadow: 0px 0px 4px rgb(255, 230, 0);}100% {box-shadow: 0px 0px 16px rgb(255, 230, 0);}
}body {position: relative;perspective: 1500px;background: url(bg2.jpg) no-repeat;background-size: cover;
}.box {position: absolute;height: 500px;width: 500px;left: 500px;top: 600px;
}.layer {position: absolute;top: 0;left: 0;width: 200px;height: 200px;transform: translateY(0);
}.cube {position: absolute;display: none;width: 100%;height: 100%;backface-visibility: visible;transform-style: preserve-3d;transform: rotateX(-30deg) rotateY(-45deg);
}.snow {position: absolute;display: none;width: 100%;height: 100%;backface-visibility: visible;transform-style: preserve-3d;transform: translateY(-8px) rotateX(-30deg) rotateY(-45deg);
}.dec {position: absolute;display: none;width: 100%;height: 100%;backface-visibility: visible;transform-style: preserve-3d;transform: translateY(-16px) rotateX(-30deg) rotateY(-45deg);
}.face {display: block;position: absolute;border: 1px solid #000;
}.leaves .face,
.wood .face {width: 64px;height: 64px;background-repeat: no-repeat;
}.leaves .face {background-image: url(leaf.png);
}.wood .face {background-image: url(wood.png);
}.snow .face {background-color: #eee;height: 8px;width: 64px;border: none;
}.dec .face {background-color: orange;height: 16px;width: 16px;border: none;animation: light 2s ease-in-out infinite alternate;
}/* Define each face based on direction */.leaves .front,
.wood .front {transform: translate3d(0, 0, 32px);
}.leaves .back,
.wood .back {transform: translate3d(0, 0, -32px) rotateY(180deg);
}.leaves .right,
.wood .right {transform: translate3d(32px, 0, 0) rotateY(90deg);
}.leaves .left,
.wood .left {transform: translate3d(-32px, 0, 0) rotateY(-90deg);
}.leaves .top,
.wood .top {transform: translate3d(0, -32px, 0) rotateX(90deg);
}.leaves .bottom,
.wood .bottom {transform: translate3d(0, 32px, 0) rotateX(-90deg);
}/*snow faces*/.snow .front {transform: translate3d(0, 0, 32px);
}.snow .back {transform: translate3d(0, 0, -32px) rotateY(180deg);
}.snow .right {transform: translate3d(32px, 0, 0) rotateY(90deg);
}.snow .left {transform: translate3d(-32px, 0, 0) rotateY(-90deg);
}.snow .top {height: 64px;transform: translate3d(0, -32px, 0) rotateX(90deg);
}.snow .bottom {height: 64px;transform: translate3d(0, -24px, 0) rotateX(-90deg);
}/*dec faces*/.dec .front {transform: translate3d(0, 0, 8px);
}.dec .back {transform: translate3d(0, 0, -8px) rotateY(180deg);
}.dec .right {transform: translate3d(8px, 0, 0) rotateY(90deg);
}.dec .left {transform: translate3d(-8px, 0, 0) rotateY(-90deg);
}.dec .top {transform: translate3d(0, -8px, 0) rotateX(90deg);
}.dec .bottom {transform: translate3d(0, 8px, 0) rotateX(-90deg);
}
3.js
设想:一层绘制完成再绘制另一层,每层绘制过程中,各个方块依次下落到待定的位置上。
由于js没有sleep这样的函数,只能用setTimeout实现,导致代码变得很难看(可是没有办法)
//prepare animate
function move(object, len, callback) {console.log(object.offsetTop);if (object['timer']) return;object['timer'] = window.setInterval(function() {//stepvar velo = (len - (object.offsetTop)) / 50;velo = velo > 0 ? Math.ceil(velo) : Math.floor(velo);//stopif (object.offsetTop == len) {clearInterval(object['timer']);object['timer'] = undefined;if (callback) callback();}//moveobject.style.top = object.offsetTop + velo + 'px';console.log(object.offsetTop);}, 5)}window.addEventListener('load', function() {//initializevar box = this.document.createElement('div');box.className = 'box';this.document.body.appendChild(box);var seeds = ['face front', 'face back', 'face right', 'face left', 'face top', 'face bottom'];//leavesvar leaves = this.document.createElement('div');for (var i = 0; i < 6; i++) {var temp = this.document.createElement('div');temp.className = seeds[i];leaves.appendChild(temp);}leaves.className = 'cube leaves';//woodvar wood = this.document.createElement('div');for (var i = 0; i < 6; i++) {var temp = this.document.createElement('div');temp.className = seeds[i];wood.appendChild(temp);}wood.className = 'cube wood';//snowvar snow = this.document.createElement('div');for (var i = 0; i < 6; i++) {var temp = this.document.createElement('div');temp.className = seeds[i];snow.appendChild(temp);}snow.className = 'snow';//decsvar dec = document.createElement('div');for (var i = 0; i < 6; i++) {var temp = this.document.createElement('div');temp.className = seeds[i];dec.appendChild(temp);}dec.className = 'dec';window.addEventListener('click', function() {//layer 1var layer1 = this.document.createElement('div');layer1.className = 'layer';layer1.appendChild(wood.cloneNode(true));box.appendChild(layer1);layer1.children[0].style.display = 'block';move(layer1.children[0], 200);setTimeout(function() {//layer 2var layer2 = this.document.createElement('div');layer2.className = 'layer';layer2.style.transform = 'translateY(-64px)'for (var i = -192; i <= 192; i += 64) {for (var j = -192; j <= 192; j += 64) {var temp;if (i == 0 && j == 0) {temp = wood.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) ';layer2.appendChild(temp);} else if ((Math.abs(i) == 192 && Math.abs(j) != 192) || (Math.abs(i) != 192 && Math.abs(j) == 192)) {//leavetemp = leaves.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px)';layer2.appendChild(temp);//snowtemp = snow.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) translateY(-8px) ';layer2.appendChild(temp);} else if (Math.abs(i) <= 128 && Math.abs(j) <= 128) {//leavestemp = leaves.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) ';layer2.appendChild(temp);if (Math.abs(i) == 128 && Math.abs(j) == 128) {//snowtemp = snow.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) translateY(-8px) ';layer2.appendChild(temp);}}}}box.appendChild(layer2);var index2 = 0;setTimeout(function rec() {if (index2 == layer2.children.length) {return;}layer2.children[index2].style.display = 'block';move(layer2.children[index2], 200);index2++;setTimeout(rec, 100);}, 50);setTimeout(function() {//layer 3var layer3 = this.document.createElement('div');layer3.className = 'layer';layer3.style.transform = 'translateY(-128px)'for (var i = -128; i <= 128; i += 64) {for (var j = -128; j <= 128; j += 64) {var temp;if (i == 0 && j == 0) {temp = wood.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) ';layer3.appendChild(temp);} else if ((Math.abs(i) == 128 && Math.abs(j) != 128) || (Math.abs(i) != 128 && Math.abs(j) == 128)) {//leavetemp = leaves.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px)';layer3.appendChild(temp);//snowtemp = snow.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) translateY(-8px) ';layer3.appendChild(temp);} else if (Math.abs(i) <= 64 && Math.abs(j) <= 64) {temp = leaves.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) ';layer3.appendChild(temp);}}}box.appendChild(layer3);var index3 = 0;setTimeout(function rec() {if (index3 == layer3.children.length) {return;}layer3.children[index3].style.display = 'block';move(layer3.children[index3], 200);index3++;setTimeout(rec, 100);}, 50);setTimeout(function() {//layer 4var layer4 = this.document.createElement('div');layer4.className = 'layer';layer4.style.transform = 'translateY(-192px)'for (var i = -64; i <= 64; i += 64) {for (var j = -64; j <= 64; j += 64) {var temp;if (i == 0 && j == 0) {temp = wood.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) ';layer4.appendChild(temp);} else if ((Math.abs(i) == 64 && Math.abs(j) != 64) || (Math.abs(i) != 64 && Math.abs(j) == 64)) {//leavetemp = leaves.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px)';layer4.appendChild(temp);}}}box.appendChild(layer4);var index4 = 0;setTimeout(function rec() {if (index4 == layer4.children.length) {return;}layer4.children[index4].style.display = 'block';move(layer4.children[index4], 200);index4++;setTimeout(rec, 100);}, 50);setTimeout(function() {//layer 5var layer5 = this.document.createElement('div');layer5.className = 'layer';layer5.style.transform = 'translateY(-256px)'for (var i = -128; i <= 128; i += 64) {for (var j = -128; j <= 128; j += 64) {var temp;if (i == 0 && j == 0) {temp = wood.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) ';layer5.appendChild(temp);} else if ((Math.abs(i) == 128 && Math.abs(j) != 128) || (Math.abs(i) != 128 && Math.abs(j) == 128)) {//leavetemp = leaves.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px)';layer5.appendChild(temp);//snowtemp = snow.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) translateY(-8px) ';layer5.appendChild(temp);} else if (Math.abs(i) <= 64 && Math.abs(j) <= 64) {temp = leaves.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) ';layer5.appendChild(temp);if (Math.abs(i) == 64 && Math.abs(j) == 64) {//snowtemp = snow.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) translateY(-8px) ';layer5.appendChild(temp);}}}}box.appendChild(layer5);var index5 = 0;setTimeout(function rec() {if (index5 == layer5.children.length) {return;}layer5.children[index5].style.display = 'block';move(layer5.children[index5], 200);index5++;setTimeout(rec, 100);}, 50);setTimeout(function() {//layer 6var layer6 = this.document.createElement('div');layer6.className = 'layer';layer6.style.transform = 'translateY(-320px)'for (var i = -64; i <= 64; i += 64) {for (var j = -64; j <= 64; j += 64) {var temp;if (i == 0 && j == 0) {temp = wood.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) ';layer6.appendChild(temp);} else if ((Math.abs(i) == 64 && Math.abs(j) != 64) || (Math.abs(i) != 64 && Math.abs(j) == 64)) {//leavetemp = leaves.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px)';layer6.appendChild(temp);}}}box.appendChild(layer6);var index6 = 0;setTimeout(function rec() {if (index6 == layer6.children.length) {return;}layer6.children[index6].style.display = 'block';move(layer6.children[index6], 200);index6++;setTimeout(rec, 100);}, 50);setTimeout(function() {//layer 7var layer7 = this.document.createElement('div');layer7.className = 'layer';layer7.style.transform = 'translateY(-384px)'var temp7 = wood.cloneNode(true);temp7.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(0) translateZ(0) ';layer7.appendChild(temp7);box.appendChild(layer7);layer7.children[0].style.display = 'block';move(layer7.children[0], 200);setTimeout(function() {//layer 8var layer8 = this.document.createElement('div');layer8.className = 'layer';layer8.style.transform = 'translateY(-448px)'for (var i = -64; i <= 64; i += 64) {for (var j = -64; j <= 64; j += 64) {var temp;if (i == 0 && j == 0) {//leavetemp = leaves.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) ';layer8.appendChild(temp);//snowtemp = snow.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) translateY(-8px) ';layer8.appendChild(temp);} else if ((Math.abs(i) == 64 && Math.abs(j) != 64) || (Math.abs(i) != 64 && Math.abs(j) == 64)) {//leavetemp = leaves.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px)';layer8.appendChild(temp);//snowtemp = snow.cloneNode(true);temp.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(' + i + 'px) translateZ(' + j + 'px) translateY(-8px) ';layer8.appendChild(temp);}}}box.appendChild(layer8);var index8 = 0;setTimeout(function rec() {if (index8 == layer8.children.length) {return;}layer8.children[index8].style.display = 'block';move(layer8.children[index8], 200);index8++;setTimeout(rec, 100);}, 50);setTimeout(function() {//decorationvar arr;var dec1 = dec.cloneNode(true);dec1.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(400px) translateZ(400px) translateY(-16px)';layer2.appendChild(dec1);var dec1_1 = dec.cloneNode(true);dec1_1.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(234px) translateZ(424px) translateY(-16px)';layer2.appendChild(dec1_1);var dec2 = dec.cloneNode(true);dec2.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(178px) translateZ(356px) translateY(-16px)';layer3.appendChild(dec2);var dec2_1 = dec.cloneNode(true);dec2_1.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(419px) translateZ(378px) translateY(-54px)';layer3.appendChild(dec2_1);var dec2_2 = dec.cloneNode(true);dec2_2.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(435px) translateZ(161px) translateY(-54px)';layer3.appendChild(dec2_2);var dec3 = dec.cloneNode(true);dec3.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(327px) translateZ(384px) translateY(-16px)';layer5.appendChild(dec3);var dec4 = dec.cloneNode(true);dec4.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(236px) translateZ(295px) translateY(-16px)';layer6.appendChild(dec4);var dec5 = dec.cloneNode(true);dec5.style.transform = 'rotateX(-30deg) rotateY(-45deg) translateX(406px) translateZ(282px) translateY(-16px)';layer7.appendChild(dec5);var arr = [];arr.push(dec1);arr.push(dec1_1);arr.push(dec2);arr.push(dec2_1);arr.push(dec2_2);arr.push(dec3);arr.push(dec4);arr.push(dec5);var index9 = 0;setTimeout(function rec() {if (index9 == arr.length) {return;}console.log(index9);arr[index9].style.display = 'block';index9++;setTimeout(rec, 200);}, 50)}, 2000)}, 20)}, 500)}, 3000)}, 600)}, 3000)}, 5000)}, 50)})})
4.成果
单击浏览器窗口,运行程序:
最后,祝所有与CSDN同行的代码人们:节日快乐哦!