整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

3种方法(div法、css法、js法)制作html的旋转太极图

.说明:

推荐指数:★★★★

通过动画太极的方法,增加学习兴趣,对html的结构和css、JavaScript、div的认识和了解会逐步深入。


2.复习html的结构框架

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>html结构基本框架代码</title>
    </head>
    
    <body>
    </body>

</html>

3 div法

3.1 代码:复制下面的代码,命名为:div法.html,用浏览器打开即可。

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>div法的旋转的太极图</title>
</head>
<!--单独style,不在head和body,只是在body内有一个div容器-->
<style>
div{
    width: 0;
    /*这个高就是黑色圆形和白色圆形的直径和*/
    height: 200px;
    /*黑色太极部分的圈带动的黑色的阴影*/
    border-left: 100px solid black;
    /*白色太极部分的圈带动的白色的阴影*/
    border-right: 100px solid #fff;
    box-shadow: 0 0 15px rgba(0,0,0,.5);
    /*旋转半径100*/
    border-radius: 100px;
    /*旋转速度定义,越小越快*/
    -webkit-animation:rotation 2.5s linear infinite;
}
div:before{
    content: "";
    position: absolute;
    height: 100px;

    z-index: 1;
    border-radius: 100px;
    /*白色的小半圆*/
    border-left: 50px solid #fff;
    border-right: 50px solid #fff;
    left: -50px;
    /*黑色的小半圆,因为转动拖动黑色阴影*/
    box-shadow: 0 100px 0 black;
}
div:after{
    content: "";
    position: absolute;
    /*height是太极里面小圆圈的高30,要和border-radius30一致,才画出圆*/
    height: 30px;
    /*这个是显示小圆圈的,0就是不显示*/
    z-index: 1;
    border-radius: 30px;
    border-left: 15px solid;
    border-right: 15px solid;
    /*top和left,决定小圆圈白色和黑色的位置*/
    top: 40px;
    left: -15px;
    /*黑色太极部分里面的小白色圆圈*/
    box-shadow: 0 100px 0 white;
}
/*旋转角度函数定义*/
@-webkit-keyframes rotation {
    0% {-webkit-transform:rotate(0deg);}
    100% {-webkit-transform:rotate(-360deg);}
}
</style>

<body>
    <div></div>
</body>

</html>

3.2 效果图


4 css法

4.1 css法.html代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>css法的旋转的太极图</title>
    <!--css导入和js导入不一样,请注意-->
    <!--script-- src="./tj.css"></!--script-->
    <link rel="stylesheet" type="text/css" href="./tj.css">
</head>
<body>
    <div class="tj"></div>
    
</body>
</html>

4.2 tj.css代码:注意与上面两个文件放在同一个文件夹下


.tj{
    width: 100px;
    height: 200px;
    border: solid black;
    border-width: 2px 100px 2px 2px;
    background-color: #fff;
    border-radius: 50%;
    position: absolute;
    /*run是动起来的函数,在最后面设置和定义*/
    animation: run 2s linear infinite;
    margin: auto;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
}
.tj:before{
    content: " ";
    position: absolute;
    width: 28px;
    height: 28px;
    background-color: black;
    /*36=(100-28)/2得到的,是小白色圆圈的半径*/
    border: 36px #ffffff solid;
    border-radius: 50%;
    top: 0;
    left: 50%;
}
.tj:after{
    content: " ";
    position: absolute;
    width: 28px;
    height: 28px;
    background-color: #ffffff;
    /*36=(100-28)/2得到的,是小黑色圆圈的半径*/
    border: 36px black solid;
    border-radius: 50%;
    top: 50%;
    left: 50%;
}
/*run动起来的函数定义*/
@keyframes run{
        0%{
            transform: rotate(0deg);
        }
        100%{
            transform: rotate(360deg);
        }
    }

4.3 效果图


5 js法=就是JavaScript法

5.1 js法.html代码:

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>js法的旋转的太极图</title>
        <!--注意下面2钟都可以,主要是浏览器都支持html5-->
        <!--script-- src="script.js" type="text/javascript"></!--script-->
        <script src="./script.js"></script>
        <!--简单的css内容就这样写在下面,如果css比较复杂,则需要外部导入-->
        <style type="text/css">
            canvas{
                display: block;
                margin: 20px auto;
                
            }
        </style>
    </head>

    <body onload="main()">
        <!--画布大小,画布框的线颜色蓝色设置,solid blue是指画布外框的颜色为蓝色-->
        <canvas width="300" height="300" id="canvas"style="border:1px solid blue"></canvas>
    </body>

</html>

5.2 script.js代码:与上面html放在同一个文件夹下

//注意到没有null=0,效果是一样的
var angle = 0;
//var canvas = null;
//var ctx = null;
var canvas = 0;
var ctx = 0;

function main()
{
    window.setInterval(function()
    {
        canvas = document.getElementById("canvas");
        ctx = canvas.getContext("2d");
        // 画布大小有关
        ctx.clearRect(0, 0, 300, 300);
        // 线条宽度0~10,均可
        ctx.lineWidth = 0;
        ctx.save();
        // 旋转的中心点的坐标位置150,150
        ctx.translate(150,150);
        ctx.rotate(angle);
        // 太极黑色部分
        ctx.fillStyle = "black";
        ctx.beginPath();
        // 注意几个函数数值的关系,120,60,半径和坐标的关系,如果要缩小半径,那么坐标也需要调整
        ctx.arc(0, 0, 120, 0, Math.PI, true);
        ctx.fill();
        ctx.closePath();
        // 太极白色部分
        ctx.fillStyle = "white";
        ctx.beginPath();
        ctx.arc(0, 0, 120, 0, Math.PI, false);
        ctx.fill();
        ctx.closePath();
        // 太极黑色部分
        ctx.fillStyle = "black";
        ctx.beginPath();
        ctx.arc(60, -0.6, 60, 0, Math.PI, false);
        ctx.fill();
        ctx.closePath();
        // 太极白色部分
        ctx.fillStyle = "white";
        ctx.lineWidth = 0;
        ctx.beginPath();
        ctx.arc(-60, 0, 60, 0, Math.PI, true);
        ctx.fill();
        ctx.closePath();
        // 白色太极部分里面的小黑色圆圈
        ctx.fillStyle = "black";
        ctx.beginPath();
        //画圆的函数:-145,0是坐标,15是半径,2*Math.PI是一个圆,一个π是半圆
        ctx.arc(-60, 0, 15, 0, 2*Math.PI, false);
        ctx.fill();
        ctx.closePath();
        // 黑色太极部分里面的小白色圆圈
        ctx.fillStyle = "white";
        ctx.beginPath();
        ctx.arc(60, 0, 15, 0, 2*Math.PI, false);
        ctx.fill();
        ctx.closePath();
        // 旋转角度一次增加多少
        ctx.restore();
        angle += 0.02;
    // 50代表转速,越大越慢,越小越快
    },1);
}

5.3 效果图


6 值得收藏,慢慢回味。

现效果视频:

https://m.toutiaoimg.com/i7012628289806139918/?gd_ext_json=%7B%22enter_from%22%3A%22click_creation_center%22%2C%22category_name%22%3A%22creation_center%22%7D&enter_from=click_creation_center&category_name=creation_center&share_token=c3b59c5c-c95d-43a9-a842-4cd30e321a34&tt_from=copy_link&utm_source=copy_link&utm_medium=toutiao_android&utm_campaign=client_share

实现代码:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>旋转、缩放</title>

  <style type="text/css">
    .box {
      width: 750px;
      height: 520px;
      margin: 50px auto;
      background-image: url(狼王-灵梦狼王.jpg);
      position: relative;
      /* 溢出隐藏 */
      overflow: hidden;

    }

    .box img {
      /* 设置图片位置 */
      position: absolute;
      top: 0;
      left: 0;
    }

    .img1 {
      z-index: 100;
      /* 动画 */
      animation: image1 2s linear 1s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image1 {
      0% {
        transform: scale(1);
      }

      50% {
        /* 缩放,缩小 */
        transform: scale(0.5);
      }

      100% {
        transform: scale(0.0001);
      }
    }

    .img2 {
      z-index: 98;
      /* 动画 */
      animation: image2 2s linear 3s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image2 {
      0% {
        transform: scale(1);
      }

      50% {
        /* 缩放,缩小 */
        transform: scale(1.5);
        /* 设置不透明度 */
        opacity: 1;
      }

      100% {
        transform: scale(5);
        opacity: 0;
      }
    }

    .img3 {
      z-index: 97;
      /* 动画 */
      animation: image3 2s linear 5s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image3 {
      0% {
        transform: rotate(0deg);
      }

      50% {
        transform: rotate(180deg);
        /* 设置不透明度 */
        opacity: 1;
      }

      100% {
        transform: rotate(360deg);
        opacity: 0;
      }
    }

    .img4 {
      z-index: 96;
      /* 动画 */
      animation: image4 2s linear 7s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image4 {
      0% {
        transform: rotate(0deg);
      }

      50% {
        transform: rotate(-180deg);
        /* 设置不透明度 */
        opacity: 1;
      }

      100% {
        transform: rotate(-360deg);
        opacity: 0;
      }
    }

    .img5 {
      z-index: 95;
      /* 动画 */
      animation: image5 2s linear 9s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image5 {
      0% {
        /* 绕y轴旋转 */
        transform: rotateY(0deg);
      }

      50% {
        transform: rotateY(-90deg);
        /* 设置不透明度 */
        opacity: 1;
      }

      100% {
        transform: rotateY(-180deg);
        opacity: 0;
      }
    }

    .img6 {
      z-index: 94;
      /* 动画 */
      animation: image6 2s linear 11s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image6 {
      0% {
        transform: rotateY(0deg);
      }

      50% {
        transform: rotateY(90deg);
        /* 设置不透明度 */
        opacity: 1;
      }

      100% {
        transform: rotateY(180deg);
        opacity: 0;
      }
    }

    .img7 {
      z-index: 93;
      /* 动画 */
      animation: image7 2s linear 13s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image7 {
      0% {
        transform: rotateZ(0deg);
      }

      50% {
        transform: rotateZ(180deg);
        /* 设置不透明度 */
        opacity: 1;
      }

      100% {
        transform: rotateZ(360deg);
        opacity: 0;
      }
    }

    .img8 {
      z-index: 92;
      /* 动画 */
      animation: image8 2s linear 15s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image8 {
      0% {
        transform: rotateZ(0deg);
      }

      50% {
        transform: rotateZ(-180deg);
        /* 设置不透明度 */
        opacity: 1;
      }

      100% {
        transform: rotateZ(-360deg);
        opacity: 0;
      }
    }

    .img9 {
      z-index: 91;
      /* 动画 */
      animation: image9 2s linear 17s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image9 {
      0% {
        transform: rotateX(0deg);
      }

      50% {
        transform: rotateX(-90deg);
        /* 设置不透明度 */
        opacity: 1;
      }

      100% {
        transform: rotateX(-180deg);
        opacity: 0;
      }
    }

    .img10 {
      z-index: 90;
      /* 动画 */
      animation: image10 2s linear 19s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image10 {
      0% {
        transform: rotateX(0deg);
      }

      50% {
        transform: rotateX(90deg);
        /* 设置不透明度 */
        opacity: 1;
      }

      100% {
        transform: rotateX(180deg);
        opacity: 0;
      }
    }

    .img11 {
      z-index: 89;
      /* 动画 */
      animation: image11 2s linear 21s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: top;
    }

    @keyframes image11 {
      0% {
        transform: rotate(0deg);
      }

      50% {
        transform: rotate(90deg);
      }

      100% {
        transform: rotate(180deg);
      }
    }

    .img12 {
      z-index: 88;
      /* 动画 */
      animation: image12 2s linear 23s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: top;
    }

    @keyframes image12 {
      0% {
        transform: rotate(0deg);
      }

      50% {
        transform: rotate(-90deg);
      }

      100% {
        transform: rotate(-180deg);
      }
    }

    .img13 {
      z-index: 87;
      /* 动画 */
      animation: image13 2s linear 25s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: top;
    }

    @keyframes image13 {
      0% {
        transform: rotateX(0deg);
      }

      50% {
        transform: rotateX(-45deg);
      }

      100% {
        transform: rotateX(-90deg);
      }
    }

    .img14 {
      z-index: 86;
      /* 动画 */
      animation: image14 2s linear 27s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: top;
    }

    @keyframes image14 {
      0% {
        transform: rotateX(0deg);
        /* 设置模糊度 */
        filter: blur(0px);
      }

      50% {
        transform: rotateX(45deg);
      }

      100% {
        transform: rotateX(90deg);
        filter: blur(1);
      }
    }

    .img15 {
      z-index: 85;
      /* 动画 */
      animation: image15 2s linear 29s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: bottom;
    }

    @keyframes image15 {
      0% {
        transform: rotate(0deg);
      }

      50% {
        transform: rotate(-90deg);
      }

      100% {
        transform: rotate(-180deg);
      }
    }

    .img16 {
      z-index: 84;
      /* 动画 */
      animation: image16 2s linear 31s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: bottom;
    }

    @keyframes image16 {
      0% {
        transform: rotate(0deg);
      }

      50% {
        transform: rotate(90deg);
      }

      100% {
        transform: rotate(180deg);
      }
    }

    .img17 {
      z-index: 83;
      /* 动画 */
      animation: image17 2s linear 33s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: bottom;
    }

    @keyframes image17 {
      0% {
        transform: rotateX(0deg);
      }

      50% {
        transform: rotateX(45deg);
      }

      100% {
        transform: rotateX(90deg);
      }
    }

    .img18 {
      z-index: 82;
      /* 动画 */
      animation: image18 2s linear 35s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: bottom;
    }

    @keyframes image18 {
      0% {
        transform: rotateX(0deg);
        filter: blur(0px);
      }

      50% {
        transform: rotateX(-45deg);
      }

      100% {
        transform: rotateX(-90deg);
        filter: blur(1px);
      }
    }

    .img19 {
      z-index: 81;
      /* 动画 */
      animation: image19 2s linear 37s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: left;
    }

    @keyframes image19 {
      0% {
        transform: rotate(0deg);
      }

      50% {
        transform: rotate(-90deg);
      }

      100% {
        transform: rotate(-180deg);
      }
    }

    .img20 {
      z-index: 80;
      /* 动画 */
      animation: image20 2s linear 39s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: left;
    }

    @keyframes image20 {
      0% {
        transform: rotate(0deg);
      }

      50% {
        transform: rotate(90deg);
      }

      100% {
        transform: rotate(180deg);
      }
    }

    .img21 {
      z-index: 79;
      /* 动画 */
      animation: image21 2s linear 41s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: left;
    }

    @keyframes image21 {
      0% {
        transform: rotateY(0deg);
      }

      50% {
        transform: rotateY(45deg);
      }

      100% {
        transform: rotateY(90deg);
      }
    }

    .img22 {
      z-index: 78;
      /* 动画 */
      animation: image22 2s linear 43s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: left;
    }

    @keyframes image22 {
      0% {
        transform: rotateY(0deg);
        filter: blur(0px);
      }

      50% {
        transform: rotateY(-45deg);
      }

      100% {
        transform: rotateY(-90deg);
        filter: blur(1px);
      }
    }

    .img23 {
      z-index: 77;
      /* 动画 */
      animation: image23 2s linear 45s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: right;
    }

    @keyframes image23 {
      0% {
        transform: rotate(0deg);
      }

      50% {
        transform: rotate(-90deg);
      }

      100% {
        transform: rotate(-180deg);
      }
    }

    .img24 {
      z-index: 76;
      /* 动画 */
      animation: image24 2s linear 47s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: right;
    }

    @keyframes image24 {
      0% {
        transform: rotate(0deg);
      }

      50% {
        transform: rotate(90deg);
      }

      100% {
        transform: rotate(180deg);
      }
    }

    .img25 {
      z-index: 75;
      /* 动画 */
      animation: image25 2s linear 49s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: right;
    }

    @keyframes image25 {
      0% {
        transform: rotateY(0deg);
        filter: blur(0px);
      }

      50% {
        transform: rotateY(45deg);
      }

      100% {
        transform: rotateY(90deg);
        filter: blur(1px);
      }
    }

    .img26 {
      z-index: 74;
      /* 动画 */
      animation: image26 2s linear 51s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: right;
    }

    @keyframes image26 {
      0% {
        transform: rotateY(0deg);
      }

      50% {
        transform: rotateY(-45deg);
      }

      100% {
        transform: rotateY(-90deg);
      }
    }

    .img27 {
      z-index: 73;
      /* 动画 */
      animation: image27 2s linear 53s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: left top;
    }

    @keyframes image27 {
      0% {
        transform: rotate(0deg);
      }

      50% {
        transform: rotate(90deg);

      }

      100% {
        transform: rotate(180deg);

      }
    }

    .img28 {
      z-index: 72;
      /* 动画 */
      animation: image28 2s linear 55s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
      /* 设置旋转原点 */
      transform-origin: right top;
    }

    @keyframes image28 {
      0% {
        transform: rotate(0deg);
      }

      50% {
        transform: rotate(-90deg);

      }

      100% {
        transform: rotate(-180deg);

      }
    }

    .img29 {
      z-index: 71;
      /* 动画 */
      animation: image29 2s linear 57s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image29 {
      0% {
        transform: rotateZ(0deg) scale(1);
      }

      50% {
        transform: rotateZ(180deg) scale(0.5);

      }

      100% {
        transform: rotateZ(360deg) scale(0.0001);

      }
    }

    .img30 {
      z-index: 70;
      /* 动画 */
      animation: image30 2s linear 59s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image30 {
      0% {
        transform: rotateX(0deg) scale(1);
      }

      50% {
        transform: rotateX(180deg) scale(0.5);

      }

      100% {
        transform: rotateX(360deg) scale(0.0001);

      }
    }

    .img31 {
      z-index: 69;
      /* 动画 */
      animation: image31 2s linear 61s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image31 {
      0% {
        transform: rotateY(0deg) scale(1);
      }

      50% {
        transform: rotateY(180deg) scale(0.5);

      }

      100% {
        transform: rotateY(360deg) scale(0.0001);

      }
    }

    .img32 {
      z-index: 68;
      /* 动画 */
      animation: image32 2s linear 63s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image32 {
      0% {
        transform: scaleX(1);
      }

      50% {
        transform: scaleX(0.5);
      }

      100% {
        transform: scaleX(0.0001);
      }
    }

    .img33 {
      z-index: 67;
      /* 动画 */
      animation: image33 2s linear 65s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image33 {
      0% {
        transform: rotateY(0deg) scaleX(1);
      }

      50% {
        transform: rotateY(180deg) scaleX(0.5);
      }

      100% {
        transform: rotateY(360deg) scaleX(0.0001);
      }
    }

    .img34 {
      z-index: 66;
      /* 动画 */
      animation: image34 2s linear 67s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image34 {
      0% {
        transform: scaleY(1);
      }

      50% {
        transform: scaleY(0.5);
      }

      100% {
        transform: scaleY(0);
      }
    }

    .img35 {
      z-index: 65;
      /* 动画 */
      animation: image35 2s linear 69s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image35 {
      0% {
        transform: rotateY(0deg) scaleY(1);
      }

      50% {
        transform: rotateY(180deg) scaleY(0.5);
      }

      100% {
        transform: rotateY(360deg) scaleY(0);
      }
    }

    .img36 {
      z-index: 64;
      /* 动画 */
      animation: image36 2s linear 71s;
      /* 让动画停留在最后一帧,不回到开始处 */
      animation-fill-mode: forwards;
    }

    @keyframes image36 {
      0% {
        transform: rotate(0deg) scaleY(1);
      }

      50% {
        transform: rotate(180deg) scaleY(1.5);
        opacity: 1;
      }

      100% {
        transform: rotate(360deg) scaleY(5);
        opacity: 0;
      }
    }
   
  </style>

</head>

<body>
  <div class="box">
    <img class="img1" src="狼蛛.jpg" alt="" />
    <img class="img2" src="狼蛛-紫魅毒姬.jpg" alt="" />
    <img class="img3" src="洛神.jpg" alt="" />
    <img class="img4" src="猎魔人.jpg" alt="" />
    <img class="img5" src="猎魔人-蜂针魔女.jpg" alt="" />
    <img class="img6" src="猎魔人-原力神枪.jpg" alt="" />
    <img class="img7" src="猎魔人-挚爱甜心.jpg" alt="" />
    <img class="img8" src="罗刹郡主.jpg" alt="" />
    <img class="img9" src="罗刹郡主-玫红冰晶.jpg" alt="" />
    <img class="img10" src="罗刹郡主-耀世神皇.jpg" alt="" />
    <img class="img11" src="罗刹郡主-樱落飞翎.jpg" alt="" />
    <img class="img12" src="洛神-北境仙姬.jpg" alt="" />
    <img class="img13" src="洛神-飞羽女王.jpg" alt="" />
    <img class="img14" src="洛神-惊鸿仙子.jpg" alt="" />
    <img class="img15" src="绿野花仙.jpg" alt="" />
    <img class="img16" src="绿野花仙-精灵公主.jpg" alt="" />
    <img class="img17" src="绿野花仙-矩阵天翼.jpg" alt="" />
    <img class="img18" src="绿野花仙-绿梦天仙.jpg" alt="" />
    <img class="img19" src="魅魔公主.jpg" alt="" />
    <img class="img20" src="魅魔公主-炼金魔女.jpg" alt="" />
    <img class="img21" src="魅魔公主-梦有灵犀.jpg" alt="" />
    <img class="img22" src="魅魔公主-星幻少女.jpg" alt="" />
    <img class="img23" src="哪吒.jpg" alt="" />
    <img class="img24" src="哪吒-飞轮公主.jpg" alt="" />
    <img class="img25" src="哪吒-黄金威灵.jpg" alt="" />
    <img class="img26" src="哪吒-轮刃审判.jpg" alt="" />
    <img class="img27" src="哪吒-异界仙将.jpg" alt="" />
    <img class="img28" src="聂小倩.jpg" alt="" />
    <img class="img29" src="聂小倩-黛染幽情.jpg" alt="" />
    <img class="img30" src="聂小倩-勾魂灯使.jpg" alt="" />
    <img class="img31" src="聂小倩-海洋之心.jpg" alt="" />
    <img class="img32" src="聂小倩-绿影魔仙.jpg" alt="" />
    <img class="img33" src="聂小倩-仲夏清和.jpg" alt="" />
    <img class="img34" src="女武神.jpg" alt="" />
    <img class="img35" src="女武神-火羽流炎.jpg" alt="" />
    <img class="img36" src="女武神-金枪战神.jpg" alt="" />


  </div>

</body>

</html>

实现效果视频:

https://www.ixigua.com/i7012628289806139918/

小新 编译自 Insight Data Blog

量子位 出品 | 公众号 QbitAI

写个网页能有多麻烦?在大多数公司里,这项工作分为三步:

1. 产品经理完成用户调研任务后,列出一系列技术要求;

2. 设计师根据这些要求来设计低保真原型,逐渐修改得到高保真原型和UI设计图;

3. 工程师将这些设计图实现为代码,最终变成用户使用的产品。

这么多环节,任何地方出一点问题,都会拉长开发周期。因此,不少公司,比如Airbnb已经开始用机器学习来提高这个过程的效率。

Airbnb内部的AI工具,从图纸到代码一步到位

看起来很美好,但Airbnb还没公开该模型中端到端训练的细节,以及手工设计的图像特征对该模型的贡献度。这是该公司特有的闭源解决方案专利,可能不会进行公开。

好在,一个叫Ashwin Kumar的程序员创建了一个开源版本,让开发者/设计师的工作变得更简单。

以下内容翻译自他的博客:

理想上,这个模型可以根据网站设计的简单手绘原型,很快地生成一个可用的HTML网站:

SketchCode模型利用手绘线框图来生成HTML网站

事实上,上面例子就是利用训练好的模型在测试集上生成的一个实际网站,代码请访问:https://github.com/ashnkumar/sketch-code。

从图像标注中获取灵感

目前要解决的问题属于一种更广泛的任务,叫做程序综合(program synthesis),即自动生成工作源代码。尽管很多程序综合研究通过自然语言规范或执行追踪法来生成代码,但在当前任务中,我会充分利用源图像,即给出的手绘线框图来展开工作。

在机器学习中有一个十分热门的研究领域,称为图像标注(image caption),目的是构建一种把图像和文本连接在一起的模型,特别是用于生成源图像内容的描述。

图像标注模型生成源图像的文本描述

我从一篇pix2code论文和另一个应用这种方法的相关项目中获得灵感,决定把我的任务按照图像标注方式来实现,把绘制的网站线框图作为输入图像,并将其相应的HTML代码作为其输出内容。

注:上段提到的两个参考项目分别是

pix2code论文:https://arxiv.org/abs/1705.07962

floydhub教程:https://blog.floydhub.com/turning-design-mockups-into-code-with-deep-learning/?source=techstories.org

获取合适的数据集

确定图像标注方法后,理想中使用的训练数据集会包含成千上万对手绘线框图和对应的HTML输出代码。但是,目前还没有我想要的相关数据集,我只好为这个任务来创建数据集。

最开始,我尝试了pix2code论文给出的开源数据集,该数据集由1750张综合生成网站的截图及其相应源代码组成。

pix2code数据集中的生成网站图片和源代码

这是一个很好的数据集,有几个有趣的地方:

  • 该数据集中的每个生成网站都包含几个简单的辅助程序元素,如按钮、文本框和DIV对象。尽管这意味着这个模型受限于将这些少数元素作为它的输出内容,但是这些元素可通过选择生成网络来修改和扩展。这种方法应该很容易地推广到更大的元素词汇表。

  • 每个样本的源代码都是由领域专用语言(DSL)的令牌组成,这是该论文作者为该任务所创建的。每个令牌对应于HTML和CSS的一个片段,且加入编译器把DSL转换为运行的HTML代码。

彩色网站图像变手绘图

为了修改我的任务数据集,我要让网站图像看起来像手工绘制出的。我尝试使用Python中的OpenCV库和PIL库等工具对每张图像进行修改,包括灰度转换和轮廓检测。

最终,我决定直接修改原始网站的CSS样式表,通过执行以下操作:

1. 更改页面上元素的边框半径来平滑按钮和DIV对象的边缘;

2. 模仿绘制的草图来调整边框的粗细,并添加阴影;

3. 将原有字体更改为类似手写的字体;

最终实现的流程中还增加了一个步骤,通过添加倾斜、移动和旋转来实现图像增强,来模拟实际绘制草图中的变化。

使用图像标注模型架构

现在,我已经处理好数据集,接下来是构建模型。

我利用了图像标注中使用的模型架构,该架构由三个主要部分组成:

1. 一种使用卷积神经网络(CNN)的计算机视觉模型,从源图像提取图像特征;

2. 一种包含门控单元GRU的语言模型,对源代码令牌序列进行编码;

3. 一个解码器模型,也属于GRU单元,把前两个步骤的输出作为输入,并预测序列中的下一个令牌。

以令牌序列为输入来训练模型

为了训练模型,我将源代码拆分为令牌序列。模型的输入为单个部分序列及它的源图像,其标签是文本中的下一个令牌。该模型使用交叉熵函数作为损失函数,将模型的下个预测令牌与实际的下个令牌进行比较。

在模型从头开始生成代码的过程中,该推理方式稍有不同。图像仍然通过CNN网络进行处理,但文本处理开始时仅采用一个启动序列。在每个步骤中,模型对序列中输出的下个预测令牌将会添加到当前输入序列,并作为新的输入序列送到模型中;重复此操作直到模型的预测令牌为,或该过程达到每个文本中令牌数目的预定义值。

当模型生成一组预测令牌后,编译器就会将DSL令牌转换为HTML代码,这些HTML代码可以在任何浏览器中运行。

用BLEU分数评估模型

我决定使用BLEU分数来评估模型。这是机器翻译任务中常用的一种度量标准,通过在给定相同输入的情况下,衡量机器生成的文本与人类可能产生内容的近似程度。

实际上,BLEU通过比较生成文本和参考文本的N元序列,以创建修改后的准确版本。它非常适用于这个项目,因为它会影响生成HTML代码中的实际元素,以及它们之间的相互关系。

最棒的是,我还可以通过检查生成的网站来比较当前的实际BLEU分数。

观察BLEU分数

当BLEU分数为1.0时,则说明给定源图像后该模型能在正确位置设置合适的元素,而较低的BLEU分数这说明模型预测了错误元素或是把它们放在相对不合适的位置。我们最终模型在评估数据集上的BLEU分数为0.76。

福利:定制网页风格

后来,我还想到,由于该模型只生成当前页面的框架,即文本的令牌,因此我可以在编译过程中添加一个定制的CSS层,并立刻得到不同风格的生成网站。

一个手绘图生成多种风格的网页

把风格定制和模型生成两个过程分开,在使用模型时带来了很多好处:

1.如果想要将SketchCode模型应用到自己公司的产品中,前端工程师可以直接使用该模型,只需更改一个CSS文件来匹配该公司的网页设计风格;

2. 该模型内置的可扩展性,即通过单一源图像,模型可以迅速编译出多种不同的预定义风格,因此用户可以设想出多种可能的网站风格,并在浏览器中浏览这些生成网页。

总结和展望

受到图像标注研究的启发,SketchCode模型能够在几秒钟内将手绘网站线框图转换为可用的HTML网站。

但是,该模型还存在一些问题,这也是我接下来可能的工作方向:

1. 由于这个模型只使用了16个元素进行训练,所以它不能预测这些数据以外的令牌。下一步方向可能是使用更多元素来生成更多的网站样本,包括网站图片,下拉菜单和窗体,可参考启动程序组件(https://getbootstrap.com/docs/4.0/components/buttons/)来获得思路;

2. 在实际网站构建中,存在很多变化。创建一个能更好反映这种变化的训练集,是提高生成效果的一种好方法,可以通过获取更多网站的HTML/CSS代码以及内容截图来提高;

3. 手绘图纸也存在很多CSS修改技巧无法捕捉到的变化。解决这个问题的一种好方法是使用生成对抗网络GAN来创建更逼真的绘制网站图像。

相关地址

代码:https://github.com/ashnkumar/sketch-code

原文:https://blog.insightdatascience.com/automated-front-end-development-using-deep-learning-3169dd086e82

— 完 —

诚挚招聘

量子位正在招募编辑/记者,工作地点在北京中关村。期待有才气、有热情的同学加入我们!相关细节,请在量子位公众号(QbitAI)对话界面,回复“招聘”两个字。

量子位 QbitAI · 头条号签约作者

վ'ᴗ' ի 追踪AI技术和产品新动态