Canvas实现3D效果

3D 球

效果图

代码

复制代码
var canvas = document.getElementById("cas"),
    ctx = canvas.getContext("2d"),
    vpx = canvas.width / 2,
    vpy = canvas.height / 2,
    Radius = 150,
    balls = [],
    angleX = Math.PI / 1000,
    angleY = Math.PI / 1000,

    factor = 0.0001 //旋转因子


var Animation = function () {
    this.init();
};
Animation.prototype = {
    init: function () {
        balls = [];
        var num = 500;
        for (var i = 0; i <= num; i++) {
            var k = -1 + (2 * (i + 1) - 1) / num;
            var a = Math.acos(k);
            var b = a * Math.sqrt(num * Math.PI);
            var x = Radius * Math.sin(a) * Math.cos(b);
            var y = Radius * Math.sin(a) * Math.sin(b);
            var z = Radius * Math.cos(a);
            var b = new ball(x, y, z, 1.5);
            balls.push(b);
            b.paint();
        }
    }
}

function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    rotateX();
    rotateY();
    balls.sort(function (a, b) {
        return b.z - a.z;
    })
    for (var i = 0; i < balls.length; i++) {
        balls[i].paint();
    }
}

function rotateX() {
    var cos = Math.cos(angleX);
    var sin = Math.sin(angleX);
    for (var i = 0; i < balls.length; i++) {
        var y1 = balls[i].y * cos - balls[i].z * sin;
        var z1 = balls[i].z * cos + balls[i].y * sin;
        balls[i].y = y1;
        balls[i].z = z1;
    }
}

function rotateY() {
    var cos = Math.cos(angleY);
    var sin = Math.sin(angleY);
    for (var i = 0; i < balls.length; i++) {
        var x1 = balls[i].x * cos - balls[i].z * sin;
        var z1 = balls[i].z * cos + balls[i].x * sin;
        balls[i].x = x1;
        balls[i].z = z1;
    }
}

var ball = function (x, y, z, r) {
    this.x = x;
    this.y = y;
    this.z = z;
    this.r = r;
    this.width = 2 * r;
}

ball.prototype = {
    paint: function () {
        var fl = 450 //焦距
        ctx.save();
        ctx.beginPath();
        var scale = fl / (fl - this.z);
        var alpha = (this.z + Radius) / (2 * Radius);
        ctx.arc(vpx + this.x, vpy + this.y, this.r * scale, 0, 2 * Math.PI, true);
        ctx.fillStyle = "rgba(0,0,0," + (alpha + 0.5) + ")"
        ctx.fill();
        ctx.restore();
    }
}

var animation = new Animation();
canvas.addEventListener('mousedown', onMousedown)

function onMousedown() {
    window.addEventListener('mousemove', onMousemove)
    window.addEventListener('mouseup', onMouseup)
}

function onMousemove(e) {
    var x = e.clientX - canvas.offsetLeft - vpx - document.body.scrollLeft - document.documentElement
        .scrollLeft;
    var y = e.clientY - canvas.offsetTop - vpy - document.body.scrollTop - document.documentElement
        .scrollTop;
    angleY = -x * factor;
    angleX = -y * factor;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    rotateX();
    rotateY();
    balls.sort(function (a, b) {
        return b.z - a.z;
    })
    for (var i = 0; i < balls.length; i++) {
        balls[i].paint();
    }
}

function onMouseup() {
    window.removeEventListener('mousemove', onMousemove)
    window.removeEventListener('mouseup', onMouseup)
}

参考链接

https://tool.4xseo.com/a/2285.html

相关推荐
颜酱2 小时前
二叉树分解问题思路解题模式
javascript·后端·算法
炫饭第一名3 小时前
速通Canvas指北🦮——路径与形状篇
前端·javascript·程序员
无责任此方_修行中3 小时前
如何利用 pnpm 的安全控制功能防御 npm 供应链攻击
javascript·npm·node.js
进击的尘埃3 小时前
前端状态管理的本质:从 Vuex 到 Pinia,我们到底在管理什么?
javascript
码路飞3 小时前
GPT-5.3 Instant 终于学会好好说话了,顺手对比了下同天发布的 Gemini 3.1 Flash-Lite
java·javascript
Lee川3 小时前
从回调地狱到同步之美:JavaScript异步编程的演进之路
javascript·面试
进击的尘埃3 小时前
WebSocket 长连接方案设计:从心跳保活到断线重连的生产级实践
javascript
摸鱼的春哥5 小时前
Agent教程15:认识LangChain(中),状态机思维
前端·javascript·后端
明月_清风5 小时前
告别遮挡:用 scroll-padding 实现优雅的锚点跳转
前端·javascript
明月_清风5 小时前
原生 JS 侧边栏缩放:从 DOM 监听到底层优化
前端·javascript