两款好看的电子宠物HTML源码

源码介绍

这是两款电子宠物HTML源码,一个小虫子,另外一个是小蜘蛛,可以放到自己的网站上面增加动态效果装饰,可以自己移动也可以跟随鼠标,喜欢的朋友拿去使用吧

蓝色小虫子

图片[1]-两款好看的电子宠物HTML源码-QQ沐编程

代码如下:

<!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>苏晨博客网</title>
    <style>
        body {
            margin: 0px;
            padding: 0px;
            position: fixed;
            /* 网页背景色 */
            background: rgb(0, 0, 0);
        }
    </style>
</head>

<body>
    <canvas id="canvas"></canvas>



    <script>
        // 定义requestAnimFrame函数
        window.requestAnimFrame = function () {
            // 检查浏览器是否支持requestAnimFrame函数
            return (
                window.requestAnimationFrame ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                window.oRequestAnimationFrame ||
                window.msRequestAnimationFrame ||
                // 如果所有这些选项都不可用,使用设置超时来调用回调函数
                function (callback) {
                    window.setTimeout(callback)
                }
            )
        }

        // 初始化函数,用于获取canvas元素并返回相关信息
        function init(elemid) {
            // 获取canvas元素
            let canvas = document.getElementById(elemid)
            // 获取2d绘图上下文,这里d是小写的
            c = canvas.getContext('2d')
            // 设置canvas的宽度为窗口内宽度,高度为窗口内高度
            w = (canvas.width = window.innerWidth)
            h = (canvas.height = window.innerHeight)
            // 设置填充样式为半透明黑
            c.fillStyle = "rgba(30,30,30,1)"
            // 使用填充样式填充整个canvas
            c.fillRect(0, 0, w, h)
            // 返回绘图上下文和canvas元素
            return { c: c, canvas: canvas }
        }

        // 等待页面加载完成后执行函数
        window.onload = function () {
            // 获取绘图上下文和canvas元素
            let c = init("canvas").c,
                canvas = init("canvas").canvas,
                // 设置canvas的宽度为窗口内宽度,高度为窗口内高度
                w = (canvas.width = window.innerWidth),
                h = (canvas.height = window.innerHeight),
                // 初始化鼠标对象
                mouse = { x: false, y: false },
                last_mouse = {}

            // 定义计算两点距离的函数
            function dist(p1x, p1y, p2x, p2y) {
                return Math.sqrt(Math.pow(p2x - p1x, 2) + Math.pow(p2y - p1y, 2))
            }

            // 定义 segment 类
            class segment {
                // 构造函数,用于初始化 segment 对象
                constructor(parent, l, a, first) {
                    // 如果是第一条触手段,则位置坐标为触手顶部位置
                    // 否则位置坐标为上一个segment对象的nextPos坐标
                    this.first = first
                    if (first) {
                        this.pos = {
                            x: parent.x,
                            y: parent.y,
                        }
                    } else {
                        this.pos = {
                            x: parent.nextPos.x,
                            y: parent.nextPos.y,
                        }
                    }
                    // 设置segment的长度和角度
                    this.l = l
                    this.ang = a
                    // 计算下一个segment的坐标位置
                    this.nextPos = {
                        x: this.pos.x + this.l * Math.cos(this.ang),
                        y: this.pos.y + this.l * Math.sin(this.ang),
                    }
                }
                // 更新segment位置的方法
                update(t) {
                    // 计算segment与目标点的角度
                    this.ang = Math.atan2(t.y - this.pos.y, t.x - this.pos.x)
                    // 根据目标点和角度更新位置坐标
                    this.pos.x = t.x + this.l * Math.cos(this.ang - Math.PI)
                    this.pos.y = t.y + this.l * Math.sin(this.ang - Math.PI)
                    // 根据新的位置坐标更新nextPos坐标
                    this.nextPos.x = this.pos.x + this.l * Math.cos(this.ang)
                    this.nextPos.y = this.pos.y + this.l * Math.sin(this.ang)
                }
                // 将 segment 回执回初始位置的方法
                fallback(t) {
                    // 将位置坐标设置为目标点坐标
                    this.pos.x = t.x
                    this.pos.y = t.y
                    this.nextPos.x = this.pos.x + this.l * Math.cos(this.ang)
                    this.nextPos.y = this.pos.y + this.l * Math.sin(this.ang)
                }
                show() {
                    c.lineTo(this.nextPos.x, this.nextPos.y)
                }
            }

            // 定义 tentacle 类
            class tentacle {
                // 构造函数,用于初始化 tentacle 对象
                constructor(x, y, l, n, a) {
                    // 设置触手的顶部位置坐标
                    this.x = x
                    this.y = y
                    // 设置触手的长度
                    this.l = l
                    // 设置触手的段数
                    this.n = n
                    // 初始化触手的目标点对象
                    this.t = {}
                    // 设置触手的随机移动参数
                    this.rand = Math.random()
                    // 创建触手的第一条段
                    this.segments = [new segment(this, this.l / this.n, 0, true)]
                    // 创建其他的段
                    for (let i = 1; i < this.n; i++) {
                        this.segments.push(
                            new segment(this.segments[i - 1], this.l / this.n, 0, false)
                        )
                    }
                }
                // 移动触手到目标点的方法
                move(last_target, target) {
                    // 计算触手顶部与目标点的角度
                    this.angle = Math.atan2(target.y - this.y, target.x - this.x)
                    // 计算触手的距离参数
                    this.dt = dist(last_target.x, last_target.y, target.x, target.y)
                    // 计算触手的目标点坐标
                    this.t = {
                        x: target.x - 0.8 * this.dt * Math.cos(this.angle),
                        y: target.y - 0.8 * this.dt * Math.sin(this.angle)
                    }
                    // 如果计算出了目标点,则更新最后一个segment对象的位置坐标
                    // 否则,更新最后一个segment对象的位置坐标为目标点坐标
                    if (this.t.x) {
                        this.segments[this.n - 1].update(this.t)
                    } else {
                        this.segments[this.n - 1].update(target)
                    }
                    // 遍历所有segment对象,更新它们的位置坐标
                    for (let i = this.n - 2; i >= 0; i--) {
                        this.segments[i].update(this.segments[i + 1].pos)
                    }
                    if (
                        dist(this.x, this.y, target.x, target.y) <=
                        this.l + dist(last_target.x, last_target.y, target.x, target.y)
                    ) {
                        this.segments[0].fallback({ x: this.x, y: this.y })
                        for (let i = 1; i < this.n; i++) {
                            this.segments[i].fallback(this.segments[i - 1].nextPos)
                        }
                    }
                }
                show(target) {
                    // 如果触手与目标点的距离小于触手的长度,则回执触手
                    if (dist(this.x, this.y, target.x, target.y) <= this.l) {
                        // 设置全局合成操作为lighter
                        c.globalCompositeOperation = "lighter"
                        // 开始新路径
                        c.beginPath()
                        // 从触手起始位置开始绘制线条
                        c.moveTo(this.x, this.y)
                        // 遍历所有的segment对象,并使用他们的show方法回执线条
                        for (let i = 0; i < this.n; i++) {
                            this.segments[i].show()
                        }
                        // 设置线条样式
                        c.strokeStyle = "hsl(" + (this.rand * 60 + 180) +
                            ",100%," + (this.rand * 60 + 25) + "%)"
                        // 设置线条宽度
                        c.lineWidth = this.rand * 2
                        // 设置线条端点样式
                        c.lineCap = "round"
                        // 设置线条连接处样式
                        c.lineJoin = "round"
                        // 绘制线条
                        c.stroke()
                        // 设置全局合成操作为“source-over”
                        c.globalCompositeOperation = "source-over"
                    }
                }
                // 绘制触手的圆形头的方法
                show2(target) {
                    // 开始新路径
                    c.beginPath()
                    // 如果触手与目标点的距离小于触手的长度,则回执白色的圆形
                    // 否则绘制青色的圆形
                    if (dist(this.x, this.y, target.x, target.y) <= this.l) {
                        c.arc(this.x, this.y, 2 * this.rand + 1, 0, 2 * Math.PI)
                        c.fillStyle = "whith"
                    } else {
                        c.arc(this.x, this.y, this.rand * 2, 0, 2 * Math.PI)
                        c.fillStyle = "darkcyan"
                    }
                    // 填充圆形
                    c.fill()
                }
            }
            // 初始化变量
            let maxl = 400,//触手的最大长度
                minl = 50,//触手的最小长度
                n = 30,//触手的段数
                numt = 600,//触手的数量
                tent = [],//触手的数组
                clicked = false,//鼠标是否被按下
                target = { x: 0, y: 0 }, //触手的目标点
                last_target = {},//上一个触手的目标点
                t = 0,//当前时间
                q = 10;//触手每次移动的步长

            // 创建触手对象
            for (let i = 0; i < numt; i++) {
                tent.push(
                    new tentacle(
                        Math.random() * w,//触手的横坐标
                        Math.random() * h,//触手的纵坐标
                        Math.random() * (maxl - minl) + minl,//触手的长度
                        n,//触手的段数
                        Math.random() * 2 * Math.PI,//触手的角度
                    )
                )
            }
            // 绘制图像的方法
            function draw() {
                // 如果鼠标移动,则计算触手的目标点与当前点的偏差
                if (mouse.x) {
                    target.errx = mouse.x - target.x
                    target.erry = mouse.y - target.y
                } else {
                    // 否则,计算触手的目标点的横坐标
                    target.errx =
                        w / 2 +
                        ((h / 2 - q) * Math.sqrt(2) * Math.cos(t)) /
                        (Math.pow(Math.sin(t), 2) + 1) -
                        target.x;
                    target.erry =
                        h / 2 +
                        ((h / 2 - q) * Math.sqrt(2) * Math.cos(t) * Math.sin(t)) /
                        (Math.pow(Math.sin(t), 2) + 1) -
                        target.y;
                }

                // 更新触手的目标点坐标
                target.x += target.errx / 10
                target.y += target.erry / 10

                // 更新时间
                t += 0.01;

                // 绘制触手的目标点
                c.beginPath();
                c.arc(
                    target.x,
                    target.y,
                    dist(last_target.x, last_target.y, target.x, target.y) + 5,
                    0,
                    2 * Math.PI
                );
                c.fillStyle = "hsl(210,100%,80%)"
                c.fill();

                // 绘制所有触手的中心点
                for (i = 0; i < numt; i++) {
                    tent[i].move(last_target, target)
                    tent[i].show2(target)
                }
                // 绘制所有触手
                for (i = 0; i < numt; i++) {
                    tent[i].show(target)
                }
                // 更新上一个触手的目标点坐标
                last_target.x = target.x
                last_target.y = target.y
            }
            // 循环执行绘制动画的函数
            function loop() {
                // 使用requestAnimFrame函数循环执行
                window.requestAnimFrame(loop)

                // 清空canvas
                c.clearRect(0, 0, w, h)

                // 绘制动画
                draw()
            }

            // 监听窗口大小改变事件
            window.addEventListener("resize", function () {
                // 重置canvas的大小
                w = canvas.width = window.innerWidth
                w = canvas.height = window.innerHeight

                // 循环执行回执动画的函数
                loop()
            })

            // 循环执行回执动画的函数
            loop()
            // 使用setInterval函数循环
            setInterval(loop, 1000 / 60)

            // 监听鼠标移动事件
            canvas.addEventListener("mousemove", function (e) {
                // 记录上一次的鼠标位置
                last_mouse.x = mouse.x
                last_mouse.y = mouse.y

                // 更新点前的鼠标位置
                mouse.x = e.pageX - this.offsetLeft
                mouse.y = e.pageY - this.offsetTop
            }, false)

            // 监听鼠标离开事件
            canvas.addEventListener("mouseleave", function (e) {
                // 将mouse设为false
                mouse.x = false
                mouse.y = false
            })
        }
    </script>
</body>

</html>

小蜘蛛

图片[2]-两款好看的电子宠物HTML源码-QQ沐编程

代码如下:

<!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>苏晨博客</title>
</head>

<body>
  <canvas id="web"></canvas>
  <script>
    !function (e, t, n) { function i(n, s) { if (!t[n]) { if (!e[n]) { var o = typeof require == "function" && require; if (!s && o) return o(n, !0); if (r) return r(n, !0); throw new Error("Cannot find module '" + n + "'") } var u = t[n] = { exports: {} }; e[n][0].call(u.exports, function (t) { var r = e[n][1][t]; return i(r ? r : t) }, u, u.exports) } return t[n].exports } var r = typeof require == "function" && require; for (var s = 0; s < n.length; s++)i(n[s]); return i }({ 1: [function (require, module, exports) { var VerletJS = require("./verlet"); var constraint = require("./constraint"); require("./objects"); window.Vec2 = require("./vec2"); window.VerletJS = VerletJS; window.Particle = VerletJS.Particle; window.DistanceConstraint = constraint.DistanceConstraint; window.PinConstraint = constraint.PinConstraint; window.AngleConstraint = constraint.AngleConstraint }, { "./verlet": 2, "./constraint": 3, "./objects": 4, "./vec2": 5 }], 3: [function (require, module, exports) { exports.DistanceConstraint = DistanceConstraint; exports.PinConstraint = PinConstraint; exports.AngleConstraint = AngleConstraint; function DistanceConstraint(a, b, stiffness, distance) { this.a = a; this.b = b; this.distance = typeof distance != "undefined" ? distance : a.pos.sub(b.pos).length(); this.stiffness = stiffness } DistanceConstraint.prototype.relax = function (stepCoef) { var normal = this.a.pos.sub(this.b.pos); var m = normal.length2(); normal.mutableScale((this.distance * this.distance - m) / m * this.stiffness * stepCoef); this.a.pos.mutableAdd(normal); this.b.pos.mutableSub(normal) }; DistanceConstraint.prototype.draw = function (ctx) { ctx.beginPath(); ctx.moveTo(this.a.pos.x, this.a.pos.y); ctx.lineTo(this.b.pos.x, this.b.pos.y); ctx.strokeStyle = "#d8dde2"; ctx.stroke() }; function PinConstraint(a, pos) { this.a = a; this.pos = (new Vec2).mutableSet(pos) } PinConstraint.prototype.relax = function (stepCoef) { this.a.pos.mutableSet(this.pos) }; PinConstraint.prototype.draw = function (ctx) { ctx.beginPath(); ctx.arc(this.pos.x, this.pos.y, 6, 0, 2 * Math.PI); ctx.fillStyle = "rgba(0,153,255,0.1)"; ctx.fill() }; function AngleConstraint(a, b, c, stiffness) { this.a = a; this.b = b; this.c = c; this.angle = this.b.pos.angle2(this.a.pos, this.c.pos); this.stiffness = stiffness } AngleConstraint.prototype.relax = function (stepCoef) { var angle = this.b.pos.angle2(this.a.pos, this.c.pos); var diff = angle - this.angle; if (diff <= -Math.PI) diff += 2 * Math.PI; else if (diff >= Math.PI) diff -= 2 * Math.PI; diff *= stepCoef * this.stiffness; this.a.pos = this.a.pos.rotate(this.b.pos, diff); this.c.pos = this.c.pos.rotate(this.b.pos, -diff); this.b.pos = this.b.pos.rotate(this.a.pos, diff); this.b.pos = this.b.pos.rotate(this.c.pos, -diff) }; AngleConstraint.prototype.draw = function (ctx) { ctx.beginPath(); ctx.moveTo(this.a.pos.x, this.a.pos.y); ctx.lineTo(this.b.pos.x, this.b.pos.y); ctx.lineTo(this.c.pos.x, this.c.pos.y); var tmp = ctx.lineWidth; ctx.lineWidth = 5; ctx.strokeStyle = "rgba(255,255,0,0.2)"; ctx.stroke(); ctx.lineWidth = tmp } }, {}], 5: [function (require, module, exports) { module.exports = Vec2; function Vec2(x, y) { this.x = x || 0; this.y = y || 0 } Vec2.prototype.add = function (v) { return new Vec2(this.x + v.x, this.y + v.y) }; Vec2.prototype.sub = function (v) { return new Vec2(this.x - v.x, this.y - v.y) }; Vec2.prototype.mul = function (v) { return new Vec2(this.x * v.x, this.y * v.y) }; Vec2.prototype.div = function (v) { return new Vec2(this.x / v.x, this.y / v.y) }; Vec2.prototype.scale = function (coef) { return new Vec2(this.x * coef, this.y * coef) }; Vec2.prototype.mutableSet = function (v) { this.x = v.x; this.y = v.y; return this }; Vec2.prototype.mutableAdd = function (v) { this.x += v.x; this.y += v.y; return this }; Vec2.prototype.mutableSub = function (v) { this.x -= v.x; this.y -= v.y; return this }; Vec2.prototype.mutableMul = function (v) { this.x *= v.x; this.y *= v.y; return this }; Vec2.prototype.mutableDiv = function (v) { this.x /= v.x; this.y /= v.y; return this }; Vec2.prototype.mutableScale = function (coef) { this.x *= coef; this.y *= coef; return this }; Vec2.prototype.equals = function (v) { return this.x == v.x && this.y == v.y }; Vec2.prototype.epsilonEquals = function (v, epsilon) { return Math.abs(this.x - v.x) <= epsilon && Math.abs(this.y - v.y) <= epsilon }; Vec2.prototype.length = function (v) { return Math.sqrt(this.x * this.x + this.y * this.y) }; Vec2.prototype.length2 = function (v) { return this.x * this.x + this.y * this.y }; Vec2.prototype.dist = function (v) { return Math.sqrt(this.dist2(v)) }; Vec2.prototype.dist2 = function (v) { var x = v.x - this.x; var y = v.y - this.y; return x * x + y * y }; Vec2.prototype.normal = function () { var m = Math.sqrt(this.x * this.x + this.y * this.y); return new Vec2(this.x / m, this.y / m) }; Vec2.prototype.dot = function (v) { return this.x * v.x + this.y * v.y }; Vec2.prototype.angle = function (v) { return Math.atan2(this.x * v.y - this.y * v.x, this.x * v.x + this.y * v.y) }; Vec2.prototype.angle2 = function (vLeft, vRight) { return vLeft.sub(this).angle(vRight.sub(this)) }; Vec2.prototype.rotate = function (origin, theta) { var x = this.x - origin.x; var y = this.y - origin.y; return new Vec2(x * Math.cos(theta) - y * Math.sin(theta) + origin.x, x * Math.sin(theta) + y * Math.cos(theta) + origin.y) }; Vec2.prototype.toString = function () { return "(" + this.x + ", " + this.y + ")" }; function test_Vec2() { var assert = function (label, expression) { console.log("Vec2(" + label + "): " + (expression == true ? "PASS" : "FAIL")); if (expression != true) throw "assertion failed" }; assert("equality", new Vec2(5, 3).equals(new Vec2(5, 3))); assert("epsilon equality", new Vec2(1, 2).epsilonEquals(new Vec2(1.01, 2.02), .03)); assert("epsilon non-equality", !new Vec2(1, 2).epsilonEquals(new Vec2(1.01, 2.02), .01)); assert("addition", new Vec2(1, 1).add(new Vec2(2, 3)).equals(new Vec2(3, 4))); assert("subtraction", new Vec2(4, 3).sub(new Vec2(2, 1)).equals(new Vec2(2, 2))); assert("multiply", new Vec2(2, 4).mul(new Vec2(2, 1)).equals(new Vec2(4, 4))); assert("divide", new Vec2(4, 2).div(new Vec2(2, 2)).equals(new Vec2(2, 1))); assert("scale", new Vec2(4, 3).scale(2).equals(new Vec2(8, 6))); assert("mutable set", new Vec2(1, 1).mutableSet(new Vec2(2, 3)).equals(new Vec2(2, 3))); assert("mutable addition", new Vec2(1, 1).mutableAdd(new Vec2(2, 3)).equals(new Vec2(3, 4))); assert("mutable subtraction", new Vec2(4, 3).mutableSub(new Vec2(2, 1)).equals(new Vec2(2, 2))); assert("mutable multiply", new Vec2(2, 4).mutableMul(new Vec2(2, 1)).equals(new Vec2(4, 4))); assert("mutable divide", new Vec2(4, 2).mutableDiv(new Vec2(2, 2)).equals(new Vec2(2, 1))); assert("mutable scale", new Vec2(4, 3).mutableScale(2).equals(new Vec2(8, 6))); assert("length", Math.abs(new Vec2(4, 4).length() - 5.65685) <= 1e-5); assert("length2", new Vec2(2, 4).length2() == 20); assert("dist", Math.abs(new Vec2(2, 4).dist(new Vec2(3, 5)) - 1.4142135) <= 1e-6); assert("dist2", new Vec2(2, 4).dist2(new Vec2(3, 5)) == 2); var normal = new Vec2(2, 4).normal(); assert("normal", Math.abs(normal.length() - 1) <= 1e-5 && normal.epsilonEquals(new Vec2(.4472, .89443), 1e-4)); assert("dot", new Vec2(2, 3).dot(new Vec2(4, 1)) == 11); assert("angle", new Vec2(0, -1).angle(new Vec2(1, 0)) * (180 / Math.PI) == 90); assert("angle2", new Vec2(1, 1).angle2(new Vec2(1, 0), new Vec2(2, 1)) * (180 / Math.PI) == 90); assert("rotate", new Vec2(2, 0).rotate(new Vec2(1, 0), Math.PI / 2).equals(new Vec2(1, 1))); assert("toString", new Vec2(2, 4) == "(2, 4)") } }, {}], 4: [function (require, module, exports) { var VerletJS = require("./verlet"); var Particle = VerletJS.Particle; var constraints = require("./constraint"); var DistanceConstraint = constraints.DistanceConstraint; VerletJS.prototype.point = function (pos) { var composite = new this.Composite; composite.particles.push(new Particle(pos)); this.composites.push(composite); return composite }; VerletJS.prototype.lineSegments = function (vertices, stiffness) { var i; var composite = new this.Composite; for (i in vertices) { composite.particles.push(new Particle(vertices[i])); if (i > 0) composite.constraints.push(new DistanceConstraint(composite.particles[i], composite.particles[i - 1], stiffness)) } this.composites.push(composite); return composite }; VerletJS.prototype.cloth = function (origin, width, height, segments, pinMod, stiffness) { var composite = new this.Composite; var xStride = width / segments; var yStride = height / segments; var x, y; for (y = 0; y < segments; ++y) { for (x = 0; x < segments; ++x) { var px = origin.x + x * xStride - width / 2 + xStride / 2; var py = origin.y + y * yStride - height / 2 + yStride / 2; composite.particles.push(new Particle(new Vec2(px, py))); if (x > 0) composite.constraints.push(new DistanceConstraint(composite.particles[y * segments + x], composite.particles[y * segments + x - 1], stiffness)); if (y > 0) composite.constraints.push(new DistanceConstraint(composite.particles[y * segments + x], composite.particles[(y - 1) * segments + x], stiffness)) } } for (x = 0; x < segments; ++x) { if (x % pinMod == 0) composite.pin(x) } this.composites.push(composite); return composite }; VerletJS.prototype.tire = function (origin, radius, segments, spokeStiffness, treadStiffness) { var stride = 2 * Math.PI / segments; var i; var composite = new this.Composite; for (i = 0; i < segments; ++i) { var theta = i * stride; composite.particles.push(new Particle(new Vec2(origin.x + Math.cos(theta) * radius, origin.y + Math.sin(theta) * radius))) } var center = new Particle(origin); composite.particles.push(center); for (i = 0; i < segments; ++i) { composite.constraints.push(new DistanceConstraint(composite.particles[i], composite.particles[(i + 1) % segments], treadStiffness)); composite.constraints.push(new DistanceConstraint(composite.particles[i], center, spokeStiffness)); composite.constraints.push(new DistanceConstraint(composite.particles[i], composite.particles[(i + 5) % segments], treadStiffness)) } this.composites.push(composite); return composite } }, { "./verlet": 2, "./constraint": 3 }], 2: [function (require, module, exports) { window.requestAnimFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1e3 / 60) }; var Vec2 = require("./vec2"); exports = module.exports = VerletJS; exports.Particle = Particle; exports.Composite = Composite; function Particle(pos) { this.pos = (new Vec2).mutableSet(pos); this.lastPos = (new Vec2).mutableSet(pos) } Particle.prototype.draw = function (ctx) { ctx.beginPath(); ctx.arc(this.pos.x, this.pos.y, 2, 0, 2 * Math.PI); ctx.fillStyle = "#2dad8f"; ctx.fill() }; function VerletJS(width, height, canvas) { this.width = width; this.height = height; this.canvas = canvas; this.ctx = canvas.getContext("2d"); this.mouse = new Vec2(0, 0); this.mouseDown = false; this.draggedEntity = null; this.selectionRadius = 20; this.highlightColor = "#4f545c"; this.bounds = function (particle) { if (particle.pos.y > this.height - 1) particle.pos.y = this.height - 1; if (particle.pos.x < 0) particle.pos.x = 0; if (particle.pos.x > this.width - 1) particle.pos.x = this.width - 1 }; var _this = this; this.canvas.oncontextmenu = function (e) { e.preventDefault() }; this.canvas.onmousedown = function (e) { _this.mouseDown = true; var nearest = _this.nearestEntity(); if (nearest) { _this.draggedEntity = nearest } }; this.canvas.onmouseup = function (e) { _this.mouseDown = false; _this.draggedEntity = null }; this.canvas.onmousemove = function (e) { var rect = _this.canvas.getBoundingClientRect(); _this.mouse.x = e.clientX - rect.left; _this.mouse.y = e.clientY - rect.top }; this.gravity = new Vec2(0, .2); this.friction = .99; this.groundFriction = .8; this.composites = [] } VerletJS.prototype.Composite = Composite; function Composite() { this.particles = []; this.constraints = []; this.drawParticles = null; this.drawConstraints = null } Composite.prototype.pin = function (index, pos) { pos = pos || this.particles[index].pos; var pc = new PinConstraint(this.particles[index], pos); this.constraints.push(pc); return pc }; VerletJS.prototype.frame = function (step) { var i, j, c; for (c in this.composites) { for (i in this.composites[c].particles) { var particles = this.composites[c].particles; var velocity = particles[i].pos.sub(particles[i].lastPos).scale(this.friction); if (particles[i].pos.y >= this.height - 1 && velocity.length2() > 1e-6) { var m = velocity.length(); velocity.x /= m; velocity.y /= m; velocity.mutableScale(m * this.groundFriction) } particles[i].lastPos.mutableSet(particles[i].pos); particles[i].pos.mutableAdd(this.gravity); particles[i].pos.mutableAdd(velocity) } } if (this.draggedEntity) this.draggedEntity.pos.mutableSet(this.mouse); var stepCoef = 1 / step; for (c in this.composites) { var constraints = this.composites[c].constraints; for (i = 0; i < step; ++i)for (j in constraints) constraints[j].relax(stepCoef) } for (c in this.composites) { var particles = this.composites[c].particles; for (i in particles) this.bounds(particles[i]) } }; VerletJS.prototype.draw = function () { var i, c; this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); for (c in this.composites) { if (this.composites[c].drawConstraints) { this.composites[c].drawConstraints(this.ctx, this.composites[c]) } else { var constraints = this.composites[c].constraints; for (i in constraints) constraints[i].draw(this.ctx) } if (this.composites[c].drawParticles) { this.composites[c].drawParticles(this.ctx, this.composites[c]) } else { var particles = this.composites[c].particles; for (i in particles) particles[i].draw(this.ctx) } } var nearest = this.draggedEntity || this.nearestEntity(); if (nearest) { this.ctx.beginPath(); this.ctx.arc(nearest.pos.x, nearest.pos.y, 8, 0, 2 * Math.PI); this.ctx.strokeStyle = this.highlightColor; this.ctx.stroke() } }; VerletJS.prototype.nearestEntity = function () { var c, i; var d2Nearest = 0; var entity = null; var constraintsNearest = null; for (c in this.composites) { var particles = this.composites[c].particles; for (i in particles) { var d2 = particles[i].pos.dist2(this.mouse); if (d2 <= this.selectionRadius * this.selectionRadius && (entity == null || d2 < d2Nearest)) { entity = particles[i]; constraintsNearest = this.composites[c].constraints; d2Nearest = d2 } } } for (i in constraintsNearest) if (constraintsNearest[i] instanceof PinConstraint && constraintsNearest[i].a == entity) entity = constraintsNearest[i]; return entity } }, { "./vec2": 5 }] }, {}, [1]);

    function getViewport() {

      var viewPortWidth;
      var viewPortHeight;

      // the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight
      if (typeof window.innerWidth != 'undefined') {
        viewPortWidth = window.innerWidth,
          viewPortHeight = window.innerHeight
      }

      // IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
      else if (typeof document.documentElement != 'undefined'
        && typeof document.documentElement.clientWidth !=
        'undefined' && document.documentElement.clientWidth != 0) {
        viewPortWidth = document.documentElement.clientWidth,
          viewPortHeight = document.documentElement.clientHeight
      }

      // older versions of IE
      else {
        viewPortWidth = document.getElementsByTagName('body')[0].clientWidth,
          viewPortHeight = document.getElementsByTagName('body')[0].clientHeight
      }
      return [viewPortWidth, viewPortHeight];
    }

    VerletJS.prototype.spider = function (origin) {
      var i;
      var legSeg1Stiffness = 0.99;
      var legSeg2Stiffness = 0.99;
      var legSeg3Stiffness = 0.99;
      var legSeg4Stiffness = 0.99;

      var joint1Stiffness = 1;
      var joint2Stiffness = 0.4;
      var joint3Stiffness = 0.9;

      var bodyStiffness = 1;
      var bodyJointStiffness = 1;

      var composite = new this.Composite();
      composite.legs = [];


      composite.thorax = new Particle(origin);
      composite.head = new Particle(origin.add(new Vec2(0, -5)));
      composite.abdomen = new Particle(origin.add(new Vec2(0, 10)));

      composite.particles.push(composite.thorax);
      composite.particles.push(composite.head);
      composite.particles.push(composite.abdomen);

      composite.constraints.push(new DistanceConstraint(composite.head, composite.thorax, bodyStiffness));


      composite.constraints.push(new DistanceConstraint(composite.abdomen, composite.thorax, bodyStiffness));
      composite.constraints.push(new AngleConstraint(composite.abdomen, composite.thorax, composite.head, 0.4));


      // legs
      for (i = 0; i < 4; ++i) {
        composite.particles.push(new Particle(composite.particles[0].pos.add(new Vec2(3, (i - 1.5) * 3))));
        composite.particles.push(new Particle(composite.particles[0].pos.add(new Vec2(-3, (i - 1.5) * 3))));

        var len = composite.particles.length;

        composite.constraints.push(new DistanceConstraint(composite.particles[len - 2], composite.thorax, legSeg1Stiffness));
        composite.constraints.push(new DistanceConstraint(composite.particles[len - 1], composite.thorax, legSeg1Stiffness));


        var lenCoef = 1;
        if (i == 1 || i == 2)
          lenCoef = 0.7;
        else if (i == 3)
          lenCoef = 0.9;

        composite.particles.push(new Particle(composite.particles[len - 2].pos.add((new Vec2(20, (i - 1.5) * 30)).normal().mutableScale(20 * lenCoef))));
        composite.particles.push(new Particle(composite.particles[len - 1].pos.add((new Vec2(-20, (i - 1.5) * 30)).normal().mutableScale(20 * lenCoef))));

        len = composite.particles.length;
        composite.constraints.push(new DistanceConstraint(composite.particles[len - 4], composite.particles[len - 2], legSeg2Stiffness));
        composite.constraints.push(new DistanceConstraint(composite.particles[len - 3], composite.particles[len - 1], legSeg2Stiffness));

        composite.particles.push(new Particle(composite.particles[len - 2].pos.add((new Vec2(20, (i - 1.5) * 50)).normal().mutableScale(20 * lenCoef))));
        composite.particles.push(new Particle(composite.particles[len - 1].pos.add((new Vec2(-20, (i - 1.5) * 50)).normal().mutableScale(20 * lenCoef))));

        len = composite.particles.length;
        composite.constraints.push(new DistanceConstraint(composite.particles[len - 4], composite.particles[len - 2], legSeg3Stiffness));
        composite.constraints.push(new DistanceConstraint(composite.particles[len - 3], composite.particles[len - 1], legSeg3Stiffness));


        var rightFoot = new Particle(composite.particles[len - 2].pos.add((new Vec2(20, (i - 1.5) * 100)).normal().mutableScale(12 * lenCoef)));
        var leftFoot = new Particle(composite.particles[len - 1].pos.add((new Vec2(-20, (i - 1.5) * 100)).normal().mutableScale(12 * lenCoef)))
        composite.particles.push(rightFoot);
        composite.particles.push(leftFoot);

        composite.legs.push(rightFoot);
        composite.legs.push(leftFoot);

        len = composite.particles.length;
        composite.constraints.push(new DistanceConstraint(composite.particles[len - 4], composite.particles[len - 2], legSeg4Stiffness));
        composite.constraints.push(new DistanceConstraint(composite.particles[len - 3], composite.particles[len - 1], legSeg4Stiffness));


        composite.constraints.push(new AngleConstraint(composite.particles[len - 6], composite.particles[len - 4], composite.particles[len - 2], joint3Stiffness));
        composite.constraints.push(new AngleConstraint(composite.particles[len - 6 + 1], composite.particles[len - 4 + 1], composite.particles[len - 2 + 1], joint3Stiffness));

        composite.constraints.push(new AngleConstraint(composite.particles[len - 8], composite.particles[len - 6], composite.particles[len - 4], joint2Stiffness));
        composite.constraints.push(new AngleConstraint(composite.particles[len - 8 + 1], composite.particles[len - 6 + 1], composite.particles[len - 4 + 1], joint2Stiffness));

        composite.constraints.push(new AngleConstraint(composite.particles[0], composite.particles[len - 8], composite.particles[len - 6], joint1Stiffness));
        composite.constraints.push(new AngleConstraint(composite.particles[0], composite.particles[len - 8 + 1], composite.particles[len - 6 + 1], joint1Stiffness));

        composite.constraints.push(new AngleConstraint(composite.particles[1], composite.particles[0], composite.particles[len - 8], bodyJointStiffness));
        composite.constraints.push(new AngleConstraint(composite.particles[1], composite.particles[0], composite.particles[len - 8 + 1], bodyJointStiffness));
      }

      this.composites.push(composite);
      return composite;
    }

    VerletJS.prototype.spiderweb = function (origin, radius, segments, depth) {
      var stiffness = 0.6;
      var tensor = 0.3;
      var stride = (2 * Math.PI) / segments;
      var n = segments * depth;
      var radiusStride = radius / n;
      var i, c;

      var composite = new this.Composite();

      // particles
      for (i = 0; i < n; ++i) {
        var theta = i * stride + Math.cos(i * 0.4) * 0.05 + Math.cos(i * 0.05) * 0.2;
        var shrinkingRadius = radius - radiusStride * i + Math.cos(i * 0.1) * 20;

        var offy = Math.cos(theta * 2.1) * (radius / depth) * 0.2;
        composite.particles.push(new Particle(new Vec2(origin.x + Math.cos(theta) * shrinkingRadius, origin.y + Math.sin(theta) * shrinkingRadius + offy)));
      }

      for (i = 0; i < segments; i += 4)
        composite.pin(i);

      // constraints
      for (i = 0; i < n - 1; ++i) {
        // neighbor
        composite.constraints.push(new DistanceConstraint(composite.particles[i], composite.particles[i + 1], stiffness));

        // span rings
        var off = i + segments;
        if (off < n - 1)
          composite.constraints.push(new DistanceConstraint(composite.particles[i], composite.particles[off], stiffness));
        else
          composite.constraints.push(new DistanceConstraint(composite.particles[i], composite.particles[n - 1], stiffness));
      }


      composite.constraints.push(new DistanceConstraint(composite.particles[0], composite.particles[segments - 1], stiffness));

      for (c in composite.constraints)
        composite.constraints[c].distance *= tensor;

      this.composites.push(composite);
      return composite;
    }

    //+ Jonas Raoni Soares Silva
    //@ http://jsfromhell.com/array/shuffle [v1.0]
    function shuffle(o) { //v1.0
      for (var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
      return o;
    }

    VerletJS.prototype.crawl = function (leg) {

      var stepRadius = 100;
      var minStepRadius = 35;

      var spiderweb = this.composites[0];
      var spider = this.composites[1];

      var theta = spider.particles[0].pos.angle2(spider.particles[0].pos.add(new Vec2(1, 0)), spider.particles[1].pos);

      var boundry1 = (new Vec2(Math.cos(theta), Math.sin(theta)));
      var boundry2 = (new Vec2(Math.cos(theta + Math.PI / 2), Math.sin(theta + Math.PI / 2)));


      var flag1 = leg < 4 ? 1 : -1;
      var flag2 = leg % 2 == 0 ? 1 : 0;

      var paths = [];

      var i;
      for (i in spiderweb.particles) {
        if (
          spiderweb.particles[i].pos.sub(spider.particles[0].pos).dot(boundry1) * flag1 >= 0
          && spiderweb.particles[i].pos.sub(spider.particles[0].pos).dot(boundry2) * flag2 >= 0
        ) {
          var d2 = spiderweb.particles[i].pos.dist2(spider.particles[0].pos);

          if (!(d2 >= minStepRadius * minStepRadius && d2 <= stepRadius * stepRadius))
            continue;

          var leftFoot = false;
          var j;
          for (j in spider.constraints) {
            var k;
            for (k = 0; k < 8; ++k) {
              if (
                spider.constraints[j] instanceof DistanceConstraint
                && spider.constraints[j].a == spider.legs[k]
                && spider.constraints[j].b == spiderweb.particles[i]) {
                leftFoot = true;
              }
            }
          }

          if (!leftFoot)
            paths.push(spiderweb.particles[i]);
        }
      }

      for (i in spider.constraints) {
        if (spider.constraints[i] instanceof DistanceConstraint && spider.constraints[i].a == spider.legs[leg]) {
          spider.constraints.splice(i, 1);
          break;
        }
      }

      if (paths.length > 0) {
        shuffle(paths);
        spider.constraints.push(new DistanceConstraint(spider.legs[leg], paths[0], 1, 0));
      }
    }

    window.onload = function () {
      var canvas = document.getElementById("web");

      // canvas dimensions
      var width = getViewport()[0] - 50;
      var height = getViewport()[1] - 50;

      // retina
      //var dpr = window.devicePixelRatio || 1;
      var dpr = 1;
      canvas.width = width * dpr;
      canvas.height = height * dpr;
      canvas.getContext("2d").scale(dpr, dpr);

      // simulation
      var sim = new VerletJS(width, height, canvas);

      // entities
      var spiderweb = sim.spiderweb(new Vec2(width / 2, height / 2), Math.min(width, height) / 2, 20, 7);

      var spider = sim.spider(new Vec2(width / 2, -300));


      spiderweb.drawParticles = function (ctx, composite) {
        var i;
        for (i in composite.particles) {
          var point = composite.particles[i];
          ctx.beginPath();
          ctx.arc(point.pos.x, point.pos.y, 1.3, 0, 2 * Math.PI);
          ctx.fillStyle = "#7AA";

          //"#" + Math.random().toString(16).slice(2, 8);

          ctx.fill();
        }
      }


      spider.drawConstraints = function (ctx, composite) {
        var i;

        ctx.beginPath();
        ctx.arc(spider.head.pos.x, spider.head.pos.y, 4, 0, 2 * Math.PI);
        ctx.fillStyle = getColor(1);
        ctx.fill();

        ctx.beginPath();
        ctx.arc(spider.thorax.pos.x, spider.thorax.pos.y, 4, 0, 2 * Math.PI);
        ctx.fill();

        ctx.beginPath();
        ctx.arc(spider.abdomen.pos.x, spider.abdomen.pos.y, 8, 0, 2 * Math.PI);
        ctx.fill();

        for (i = 3; i < composite.constraints.length; ++i) {
          var constraint = composite.constraints[i];
          if (constraint instanceof DistanceConstraint) {
            ctx.beginPath();
            ctx.moveTo(constraint.a.pos.x, constraint.a.pos.y);
            ctx.lineTo(constraint.b.pos.x, constraint.b.pos.y);

            // draw legs
            if (
              (i >= 2 && i <= 4)
              || (i >= (2 * 9) + 1 && i <= (2 * 9) + 2)
              || (i >= (2 * 17) + 1 && i <= (2 * 17) + 2)
              || (i >= (2 * 25) + 1 && i <= (2 * 25) + 2)
            ) {
              ctx.save();
              constraint.draw(ctx);
              ctx.strokeStyle = getColor(2);
              ctx.lineWidth = 3;
              ctx.stroke();
              ctx.restore();
            } else if (
              (i >= 4 && i <= 6)
              || (i >= (2 * 9) + 3 && i <= (2 * 9) + 4)
              || (i >= (2 * 17) + 3 && i <= (2 * 17) + 4)
              || (i >= (2 * 25) + 3 && i <= (2 * 25) + 4)
            ) {
              ctx.save();
              constraint.draw(ctx);
              ctx.strokeStyle = getColor(3);
              ctx.lineWidth = 2;
              ctx.stroke();
              ctx.restore();
            } else if (
              (i >= 6 && i <= 8)
              || (i >= (2 * 9) + 5 && i <= (2 * 9) + 6)
              || (i >= (2 * 17) + 5 && i <= (2 * 17) + 6)
              || (i >= (2 * 25) + 5 && i <= (2 * 25) + 6)
            ) {
              ctx.save();
              ctx.strokeStyle = getColor(4);
              ctx.lineWidth = 1.5;
              ctx.stroke();
              ctx.restore();
            } else {
              ctx.strokeStyle = getColor(5);
              ctx.stroke();
            }
          }
        }
      }

      spider.drawParticles = function (ctx, composite) {
      }

      // animation loop
      var legIndex = 0;
      var loop = function () {
        ti++;

        if (Math.floor(Math.random() * 4) == 0) {
          sim.crawl(((legIndex++) * 3) % 8);
        }

        sim.frame(16);
        sim.draw();
        requestAnimFrame(loop);
      };

      loop();
    };

    var ti = 0;
    var tc = [
      ["#661111", "#661111", "#4D1A1A", "#332222", "#1A2B2B"], //red
      ["#663311", "#663311", "#4D2A1A", "#333022", "#1A1A2B"], //orange
      ["#666611", "#666611", "#4D4D1A", "#333322", "#1A1A2B"], //yellow
      ["#116611", "#116611", "#1A4D1A", "#223322", "#2B1A2B"], //green
      ["#111166", "#111166", "#1A1A4D", "#222233", "#2B2B1A"], //blue
      ["#661166", "#661166", "#4D1A4D", "#332233", "#1A2B1A"], //purple
      ["#111166", "#111166", "#1A1A4D", "#222233", "#2B2B1A"], //blue
      ["#116611", "#116611", "#1A4D1A", "#223322", "#2B1A2B"], //green
      ["#666611", "#666611", "#4D4D1A", "#333322", "#1A1A2B"], //yellow
      ["#663311", "#663311", "#4D2A1A", "#333022", "#1A1A2B"], //orange
      ["#661111", "#661111", "#4D1A1A", "#332222", "#1A2B2B"] //red
    ];

    function getColor(part) {
      var col = "#661111";

      if (ti >= 999) {
        ti = 0;
      }

      var ts = Math.floor(ti / 100);
      var ta = 200 - ((ti % 100) * 2);

      switch (part) {
        case 1: col = shadeColor(tc[ts][0], ta); break;
        case 2: col = shadeColor(tc[ts][1], ta); break;
        case 3: col = shadeColor(tc[ts][2], ta); break;
        case 4: col = shadeColor(tc[ts][3], ta); break;
        case 5: col = shadeColor(tc[ts][4], ta); break;
      }
      return col;
    }

    function shadeColor(color, shade) {
      var colorInt = parseInt(color.substring(1), 16);

      var R = (colorInt & 0xFF0000) >> 16;
      var G = (colorInt & 0x00FF00) >> 8;
      var B = (colorInt & 0x0000FF) >> 0;

      R = R + Math.floor((shade / 255) * R);
      G = G + Math.floor((shade / 255) * G);
      B = B + Math.floor((shade / 255) * B);

      var newColorInt = (R << 16) + (G << 8) + (B);
      var newColorStr = "#" + newColorInt.toString(16);

      return newColorStr;
    }
  </script>
</body>

</html>

 

© 版权声明
THE END
喜欢就支持一下吧
点赞14赞赏 分享