Canvas——3D旋转小球

单个球体

在这里插入图片描述

<!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>
    <style>
      body {
        background-color: black;
        overflow: hidden;
      }
      canvas {
        /* background: lightgoldenrodyellow; */
        display: block;
        margin: auto;
        position: absolute;

        /* 绝对定位设置居中 */
        top: 0;
        left: 0;
        bottom: 0;
        right: 0;
      }
    </style>
  </head>
  <body>
    <canvas id="canvas1" width="500" height="500"></canvas>
    <script>
      var canvas = document.querySelector("canvas");
      var ctx = canvas.getContext("2d");
      var cw = canvas.width,
        cx = cw / 2;

      var ch = canvas.height,
        cy = ch / 2;

      //  存放球的所有点
      var balls = [];
      var fl = 250; //所绘制圆形放大缩小的控制变量

      // 在球体上获取一个随机点
      function spherePointPicking(R) {
        //https://math.stackexchange.com/questions/1585975/how-to-generate-random-points-on-a-sphere#1586185
        var u1 = Math.random();
        var u2 = Math.random();
        // acos反余弦函数
        var s = Math.acos(2 * u1 - 1) - Math.PI / 2;
        var t = 2 * Math.PI * u2;

        return {
          x: R * Math.cos(s) * Math.cos(t),
          y: R * Math.cos(s) * Math.sin(t),
          z: R * Math.sin(s),
        };
      }

      function oGrd(r, h) {
        // 径向渐变
        grd = ctx.createRadialGradient(0, 0, 0, 0, 0, r);

        // hsla() 函数使用色相、饱和度、亮度、透明度来定义颜色。
        grd.addColorStop(0, "hsla(" + h + ",95%,95%, 1)");
        grd.addColorStop(0.4, "hsla(" + h + ",95%,45%,.5)");
        grd.addColorStop(1, "hsla(" + h + ", 95%, 45%, 0.1)");

        return grd;
      }

      function Ball(R) {
        // 所画圆的半径
        this.r = 3;
        // 所在球体的半径
        this.R = R;

        // 在球体上获取一个随机点(坐标有正有负,球心是(0,0))
        this.pos = spherePointPicking(this.R);

        // 获取所要绘制的圆的圆心
        this.x = this.pos.x + cx;
        this.y = this.pos.y + cy;
        // 所要绘制圆的缩放比例
        this.scale = { x: 1, y: 1 };

        // 根据3d的位置设置圆的缩放和可见性
        this.draw3D = function () {
          //设置缩放控制圆的大小
          if (this.pos.z > -fl) {
            var scale = fl / (fl - this.pos.z);
            this.scale = { x: scale, y: scale };
            // 更新圆心的坐标
            this.x = cx + this.pos.x * scale;
            this.y = cy + this.pos.y * scale;
            this.visible = true;
          } else {
            this.visible = false;
          }
        };

        // 画2d圆
        this.draw2D = function () {
          ctx.save();
          //将坐标轴移动到所要画的圆的圆心的位置,再进行绘制,(不知道为什么将坐标轴移动到画布中心再根据圆心的坐标进行绘制绘制出的圆比较模糊)
          ctx.translate(this.x, this.y);
          ctx.scale(this.scale.x, this.scale.y);
          ctx.beginPath();
          ctx.arc(0, 0, this.r, 0, 2 * Math.PI);
          ctx.fillStyle = oGrd(this.r, 210);
          ctx.fill();
          ctx.restore();
        };

        // 绕x轴旋转
        this.rotateX = function (angle) {
          var cos = Math.cos(angle);
          var sin = Math.sin(angle);
          var y1 = this.pos.y * cos - this.pos.z * sin;
          var z1 = this.pos.z * cos + this.pos.y * sin;

          this.pos.y = y1;
          this.pos.z = z1;
        };
        // 绕y轴旋转
        this.rotateY = function (angle) {
          var cos = Math.cos(angle);
          var sin = Math.sin(angle);
          var x1 = this.pos.x * cos - this.pos.z * sin;
          var z1 = this.pos.z * cos + this.pos.x * sin;

          this.pos.x = x1;
          this.pos.z = z1;
        };
      }

      for (let i = 0; i < 1000; i++) {
        //一个球面上随机1000个点
        balls.push(new Ball(150));
        balls.push(new Ball(75));
      }

      // 动画帧
      var frames = 0;
      //   中间变量
      var m = { x: 0, y: 0 };
      //
      var target = { x: 0, y: 0 };
      //   移动的速度,变量1
      var speed = 0.0005;
      //   移动的速度,变量2
      var easing = 0.9;

      // 画球体
      function Draw() {
        // 渲染动画帧
        window.requestAnimationFrame(Draw);
        ctx.clearRect(0, 0, cw, ch);

        frames += 0.1;

        // 根据时间随机取定下次移动的位置
        t = new Date().getTime() / 127;
        m.x = cx + Math.cos(t / 43 + Math.cos(t / 47 + frames)) * 50;
        m.y = cy + Math.sin(t / 31 + Math.cos(t / 37 + frames)) * 50;
        target.x = (m.y - cy) * speed * easing;
        target.y = (m.x - cx) * speed * easing;

        balls.map((b) => {
          // 设置圆的缩放和可见性
          b.draw3D();
        });
        balls.sort((a, b) => {
          return a.pos.z - b.pos.z;
        });

        balls.map((b) => {
          b.rotateX(target.x);
          b.rotateY(target.y);

          if (b.visible) {
            b.draw2D();
          }
        });
      }

      Draw();
    </script>
  </body>
</html>

多个球体

在这里插入图片描述

<!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>
    <style>
      body {
        background-color: black;
        overflow: hidden;
      }
      canvas {
        /* background: lightgoldenrodyellow; */
        display: block;
        margin: auto;
        position: absolute;

        /* 绝对定位设置居中 */
        top: 0;
        left: 0;
        bottom: 0;
        right: 0;
      }
    </style>
  </head>
  <body>
    <canvas id="canvas1" width="1500" height="500"></canvas>
    <script>
      var canvas = document.querySelector("canvas");
      var ctx = canvas.getContext("2d");
      var cw = canvas.width,
        cx = cw / 2;

      var ch = canvas.height,
        cy = ch / 2;

      //  存放球的所有点
      const balls = {
        balls0: [],
        balls1: [],
        balls2: [],
        balls3: [],
        balls4: [],
        balls5: [],
      };

      var fl = 250; //所绘制圆形放大缩小的控制变量

      // 在球体上获取一个随机点
      function spherePointPicking(R) {
        //https://math.stackexchange.com/questions/1585975/how-to-generate-random-points-on-a-sphere#1586185
        var u1 = Math.random();
        var u2 = Math.random();
        // acos反余弦函数
        var s = Math.acos(2 * u1 - 1) - Math.PI / 2;
        var t = 2 * Math.PI * u2;

        return {
          x: R * Math.cos(s) * Math.cos(t),
          y: R * Math.cos(s) * Math.sin(t),
          z: R * Math.sin(s),
        };
      }

      function oGrd(r, h) {
        // 径向渐变
        grd = ctx.createRadialGradient(0, 0, 0, 0, 0, r);

        // hsla() 函数使用色相、饱和度、亮度、透明度来定义颜色。
        grd.addColorStop(0, "hsla(" + h + ",95%,95%, 1)");
        grd.addColorStop(0.4, "hsla(" + h + ",95%,45%,.5)");
        grd.addColorStop(1, "hsla(" + h + ", 95%, 45%, 0.1)");

        return grd;
      }

      function Ball(R, circleX, circleY, color) {
        // 所画圆的半径
        this.r = 3;
        // 所在球体的半径
        this.R = R;

        // 在球体上获取一个随机点(坐标有正有负,球心是(0,0))
        this.pos = spherePointPicking(this.R);

        // 获取所要绘制的圆的圆心
        this.x = this.pos.x + circleX;
        this.y = this.pos.y + circleY;
        // 所要绘制圆的缩放比例
        this.scale = { x: 1, y: 1 };

        // 根据3d的位置设置圆的缩放和可见性
        this.draw3D = function () {
          //设置缩放控制圆的大小
          if (this.pos.z > -fl) {
            var scale = fl / (fl - this.pos.z);
            this.scale = { x: scale, y: scale };
            // 更新圆心的坐标
            this.x = circleX + this.pos.x * scale;
            this.y = circleY + this.pos.y * scale;
            this.visible = true;
          } else {
            this.visible = false;
          }
        };

        // 画2d圆
        this.draw2D = function () {
          ctx.save();
          //将坐标轴移动到所要画的圆的圆心的位置,再进行绘制,(不知道为什么将坐标轴移动到画布中心再根据圆心的坐标进行绘制绘制出的圆比较模糊)
          ctx.translate(this.x, this.y);
          ctx.scale(this.scale.x, this.scale.y);
          ctx.beginPath();
          ctx.arc(0, 0, this.r, 0, 2 * Math.PI);
          ctx.fillStyle = oGrd(this.r, color);
          ctx.fill();
          ctx.restore();
        };

        // 绕x轴旋转
        this.rotateX = function (angle) {
          var cos = Math.cos(angle);
          var sin = Math.sin(angle);
          var y1 = this.pos.y * cos - this.pos.z * sin;
          var z1 = this.pos.z * cos + this.pos.y * sin;

          this.pos.y = y1;
          this.pos.z = z1;
        };
        // 绕y轴旋转
        this.rotateY = function (angle) {
          var cos = Math.cos(angle);
          var sin = Math.sin(angle);
          var x1 = this.pos.x * cos - this.pos.z * sin;
          var z1 = this.pos.z * cos + this.pos.x * sin;

          this.pos.x = x1;
          this.pos.z = z1;
        };
      }

      for (let i = 0; i < 1000; i++) {
        //一个球面上随机1000个点
        balls.balls0.push(new Ball(150, cx, cy, 210));
        balls.balls0.push(new Ball(75, cx, cy, 210));
      }
      for (let i = 0; i < 800; i++) {
        balls.balls1.push(new Ball(100, cx / 2, cy / 2, 225));
        balls.balls1.push(new Ball(50, cx / 2, cy / 2, 225));
      }
      for (let i = 0; i < 300; i++) {
        balls.balls2.push(new Ball(75, (cx / 2) * 3, cy / 2, 230));
        balls.balls2.push(new Ball(50, (cx / 2) * 3, cy / 2, 230));
      }
      for (let i = 0; i < 100; i++) {
        balls.balls3.push(new Ball(25, (cx / 3) * 4, (cy / 3) * 4, 255));
        balls.balls3.push(new Ball(20, (cx / 3) * 4, (cy / 3) * 4, 255));
      }
      for (let i = 0; i < 600; i++) {
        balls.balls4.push(new Ball(90, cx / 4, (cy / 3) * 4, 200));
        balls.balls4.push(new Ball(65, cx / 4, (cy / 3) * 4, 200));
      }
      for (let i = 0; i < 500; i++) {
        balls.balls4.push(new Ball(100, (cx / 5) * 9, (cy / 7) * 10, 190));
        balls.balls4.push(new Ball(75, (cx / 5) * 9, (cy / 7) * 10, 190));
      }

      // 动画帧
      var frames = 0;
      //   中间变量
      var m = { x: 0, y: 0 };
      //
      var target = { x: 0, y: 0 };
      //   移动的速度,变量1
      var speed = 0.0005;
      //   移动的速度,变量2
      var easing = 0.9;

      // 画球体
      function Draw() {
        // 渲染动画帧
        window.requestAnimationFrame(Draw);
        ctx.clearRect(0, 0, cw, ch);

        frames += 0.1;

        // 根据时间随机取定下次移动的位置
        t = new Date().getTime() / 127;
        m.x = cx + Math.cos(t / 43 + Math.cos(t / 47 + frames)) * 50;
        m.y = cy + Math.sin(t / 31 + Math.cos(t / 37 + frames)) * 50;
        target.x = (m.y - cy) * speed * easing;
        target.y = (m.x - cx) * speed * easing;

        Object.keys(balls).forEach((item) => {
          balls[item].map((b) => {
            // 设置圆的缩放和可见性
            b.draw3D();
          });
          balls[item].sort((a, b) => {
            return a.pos.z - b.pos.z;
          });

          const random = Math.random() + 0.5;
          console.log("123", random);
          balls[item].map((b, index) => {
            b.rotateX(target.x * random);
            b.rotateY(target.y * random);

            if (b.visible) {
              b.draw2D();
            }
          });
        });
      }

      Draw();
    </script>
  </body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值