动态心形图

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href='https://fonts.googleapis.com/css?family=Lato:100' rel='stylesheet' type='text/css'>
    <canvas id="canvas" width="800" height="500"></canvas>
    <div class="title">Happy New Year!</div>
    <style type="text/css">
        body{
            background:#ef6666; margin:0;
            padding: 0;
            overflow: hidden;
        }

        canvas {
            background:#ef6666;
            position: absolute;
        }

        div.metadata {
            position: absolute;
            bottom:0;
            width: 100%;

            font-family: Helvetica, Arial;
            font-size:11px;
            font-weight: bold;
            text-align: center;
            color: rgba(151,26,22,1);
        }
        div.metadata p {
            height: 20px;
        }
        a {
            color: inherit;
            text-decoration: none;
            display:inline-block;
            padding: 0 4px 3px 4px;
            border-bottom: 2px solid rgba(151,26,22,1);
            transition: all .2s;
        }
        a:hover {
            padding: 0 4px 1px 4px;
            border-bottom: 4px solid rgba(254,121,68,0.4);
        }

        div.title {
            position: absolute;
            top:0;
            width: 100%;
            text-align:center;
            font-family: 'Lato', sans-serif !important;
            font-style: normal;
            font-weight: 100;
            font-size: 70px;
            padding-top:40px;
            color: rgba(254,255,240,1);
        }
    </style>
    <script src="js/jquery-1.11.3.min.js"></script>
</head>
<body>
<script>
    function square(x){return x*x;}
    function heart_shape(t, a) {
        var x = 16 * (Math.sin(t) * Math.sin(t) * Math.sin(t));
        var y = 13 * Math.cos(t) - 5 * Math.cos(2*t) - 2 * Math.cos(3*t) - Math.cos(4*t);
        return [x*a/12, y*a/15 * -1];
    }

    function rotate_point(pointX, pointY, originX, originY, angle, extrude) {
        var slope = Math.atan2(pointY - originY, pointX - originX);
        pointY = originY + Math.sin(slope) * extrude;
        pointX = originX + Math.cos(slope) * extrude;

        angle = angle * Math.PI / 180.0;
        return {
            x: Math.cos(angle) * (pointX-originX) - Math.sin(angle) * (pointY-originY) + originX,
            y: Math.sin(angle) * (pointX-originX) + Math.cos(angle) * (pointY-originY) + originY
        };
    }

    var canvas = document.getElementsByTagName('canvas')[0];
    var ctx = canvas.getContext('2d');

    /*
      Some global variables
    */

    var center = [400,250],
        pi = Math.PI,
        lob = heart_shape;
    /* color, maxthicknes, minthickness, maxlength*/
    var colors = [
        ["rgba(254,68,68,0.4)", 30, 20, pi/4],
        ["rgba(255,85,85,0.5)", 15, 10, pi/2],
        ["rgba(151,26,22,0.5)",  10, 8,  pi],
        ["rgba(250,240,240,0.8)", 10, 8, 1.5*pi],
        ["rgba(50,11,4,0.7)",     5, 3,  1.5*pi]
    ];

    /*
      A loop object
    */
    function InfinityLoop(){
        var pickedColor = colors[Math.round(Math.random() * (colors.length -1))];

        this.a = Math.random() * 50 + 150;
        //this.length = (Math.random() * 0.75 + 0.25) * pi; //Max = 2*PI
        this.length = Math.random() * pickedColor[3] + (0.25*pi);
        this.position = Math.random() * 2 * pi; //0.5 * pi;
        this.speed = (2 * pi / 100) - (Math.random() * ( pi / 200));
        this.heightAdjust = Math.random() * 0.2 + 1;
        this.center = {
            x: Math.random() * 20 - 10 + center[0],
            y: Math.random() * 20 - 10 + center[1],
        }



        this.color = pickedColor[0];//"rgba(200, 180, 0, 0.75)";
        this.thickness = Math.random() * pickedColor[1] + pickedColor[2];

        this.computePath = function(start, length, a) {
            var main_points = [],
                extruded_left_points = [],
                extruded_right_points = [];
            var segments = Math.round(this.length / this.speed);

            var lastPoint = null;

            for(i=0; i<segments; i++) {

                t = this.speed * i + this.position;
                if(t > 2 * pi) t = t - 2 * pi;
                if(t < 0) t = 2 * pi - t;

                var main_point = lob(t, a);
                main_point[1] = main_point[1] * this.heightAdjust + this.center.y;
                var newPoint = {
                    x: main_point[0] + this.center.x,
                    y: main_point[1]
                };

                if(lastPoint == null) {
                    var prevPoint = lob(t - this.speed, a);
                    prevPoint[1] = prevPoint[1] * this.heightAdjust + this.center.y;
                    lastPoint = {x: prevPoint[0] + this.center.x, y:prevPoint[1]};
                }

                var ribbonReductionIndex = ((i / (segments / 200)));
                //ribbonReductionIndex *= (Math.cos(2*t)+1);
                var ribbonThickness = Math.sin(pi/2*(ribbonReductionIndex / 100))*this.thickness;

                var extrudedLeftPoint = rotate_point(
                    lastPoint.x, lastPoint.y,
                    newPoint.x, newPoint.y, 90, - ribbonThickness/2
                );
                extruded_left_points.push(extrudedLeftPoint);

                var extrudedRightPoint = rotate_point(
                    lastPoint.x, lastPoint.y,
                    newPoint.x, newPoint.y, 90, ribbonThickness/2
                );
                extruded_right_points.push(extrudedRightPoint);

                lastPoint = newPoint;
            }
            var points = extruded_left_points.concat(extruded_right_points.reverse());

            return points;
        }

        this.drawPath = function() {
            var leafPath = this.computePath(
                this.position,
                this.length,
                this.a, 5);

            this.position += this.speed;

            ctx.fillStyle = this.color;
            ctx.beginPath();
            ctx.moveTo(leafPath[0].x, leafPath[0].y);
            for(i=1;i<leafPath.length;i++) {
                ctx.lineTo(leafPath[i].x, leafPath[i].y);
            }
            ctx.closePath();
            ctx.fill();

            if(this.position > 2 * pi) {
                this.position = this.position - 2 * pi;
            }
        }

        this.reposition = function(){
            this.center = {
                x: Math.random() * 20 - 10 + center[0],
                y: Math.random() * 20 - 10 + center[1],
            }
        }

        this.tick = function(timestamp){
            this.drawPath();
        }

        return this;
    }



    /*
      Get the animation started
    */
    var Scene = function(){

        var loops = [];
        var lastTick = 0

        for(var i=0; i<50; i++) {
            loops.push(new InfinityLoop());
        }

        var clearCanvas = function() {
            ctx.fillStyle="rgba(200,60,60,.1)";
            ctx.fillRect(0,0,canvas.width,canvas.height);
        }

        var animate = function(timestamp) {
            /*skip frames if more than 60/second*/
            if(timestamp - lastTick < 1000/60){
                requestAnimationFrame(animate);
                return;
            }

            clearCanvas();
            for(var i=0; i<loops.length; i++) {
                loops[i].tick(timestamp);
            }


            lastTick = timestamp;
            requestAnimationFrame(animate);
        }

        this.run = function() {
            requestAnimationFrame(animate);
        }

        this.reset = function() {
            loops = [];
            lastTick = 0

            for(var i=0; i<50; i++) {
                loops.push(new InfinityLoop());
            }
        }

        this.reposition = function() {
            for(i=0; i<loops.length; i++) {
                loops[i].reposition();
            }
        }

        return this;
    }


    var scene = new Scene();
    scene.run();

    $(window).resize(function(){
        canvas.width = $(window).width();
        canvas.height = $(window).height();
        center[0] = canvas.width/2;
        center[1] = canvas.height/2;
        scene.reposition();
    }).resize();

    function reset(){
        scene.reset();
    }
</script>
</body>
</html>

动态心形图