WebGl 如何给页面绑定点击事件

在WebGL中给页面绑定点击事件,可以通过为WebGL的绘图上下文所在的<canvas>元素添加事件监听器来实现点击事件的处理。

1. 画布添加点击事件

javascript 复制代码
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')

ctx.onclick = function (e) {
    // 给canvas添加点击事件
}

2. 获取坐标位置

getBoundingClientRect的top和left,等同于offsetTop和offsetLeft,即canvas边框到屏幕的距离

javascript 复制代码
const domPosition = e.target.getBoundingClientRect();

3. 将画布的宽高转换成坐标

javascript 复制代码
// 1.获取鼠标相对于浏览器的坐标
const x = e.clientX;
const y = e.clientY;

// 2.获取画布边框到浏览器的距离
const domPosition = e.target.getBoundingClientRect();

// 3.鼠标点击位置到canvas边框的距离
const domx = x - domPosition.left;
const domy = y - domPosition.top;

// 4.转换坐标的公式:
// 水平坐标=当前鼠标点击的坐标x-当前画布的一半,再除以当前画布的一半
// 垂直坐标=当前画布的一半-当前鼠标点击的坐标y,再除以当前画布的一半
const halfWidth = ctx.offsetWidth / 2;
const halfHeigth = ctx.offsetHeight / 2;

const clickX = (domx - halfWidth) / halfWidth;
const clickY = (halfHeigth - domy) / halfHeigth;

4. 根据坐标在画布上绘制点

javascript 复制代码
// 两个坐标点就用vertexAttrib2f
gl.vertexAttrib2f(aPosition, clickX, clickY) 
gl.drawArrays(gl.POINTS, 0, 1);

5. 完整代码

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        canvas {
            margin: 50px auto;
            display: block;
            background: pink;
        }
    </style>
    <title>webgl三维坐标系</title>
</head>

<body>
    <canvas id="canvas" width="400" height="400">
        此浏览器不支持canvas
    </canvas>

    <script>
        const ctx = document.getElementById('canvas')
        const gl = ctx.getContext('webgl')
        // 顶点着色器源码
        const vertexShaderSource = `
            attribute vec4 aPosition;
            void main() {
                gl_Position = aPosition; 
                gl_PointSize = 5.0;
            }`

        // 片源着色器源码
        const fragmentShaderSource = `
            void main() {
                gl_FragColor = vec4(0.0,0.0,0.0,1.0); // r, g, b, a
            }`

        // 设置着色器封装后,直接使用
        const program = initShader(gl, vertexShaderSource, fragmentShaderSource)
        // 返回变量的存储地址
        const aPosition = gl.getAttribLocation(program, 'aPosition');
        // 着色器方法
        function initShader(gl, vertexShaderSource, fragmentShaderSource) {
            const vertexShader = gl.createShader(gl.VERTEX_SHADER);// 创建顶点着色器对象
            const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);// 创建片段着色器对象
            gl.shaderSource(vertexShader, vertexShaderSource);// 设置顶点着色器源代码
            gl.shaderSource(fragmentShader, fragmentShaderSource);// 设置片段着色器源代码
            gl.compileShader(vertexShader);// 编译顶点着色器
            gl.compileShader(fragmentShader);// 编译片段着色器
            // 创建一个程序对象
            const program = gl.createProgram();
            gl.attachShader(program, vertexShader);
            gl.attachShader(program, fragmentShader);
            gl.linkProgram(program);
            gl.useProgram(program);
            return program;
        }
        // -----------------------------本节新增代码-------------------------------------------
        const points = [];
        // 1.给canvas添加点击事件
        ctx.onclick = function (e) {
            // 2.获取坐标位置
            const x = e.clientX;
            const y = e.clientY;
            // getBoundingClientRect的x和y,等同于offsetTop和offsetLeft,即canvas边框到屏幕的距离
            const domPosition = e.target.getBoundingClientRect();
            console.log(domPosition, ctx.offsetTop, ctx.offsetLeft)
            console.log(x, y)
            // 鼠标点击位置到canvas边框的距离
            const domx = x - domPosition.left;
            const domy = y - domPosition.top;
            // 3.将获取当前画布的宽(0,200,400)转换成坐标(-1,0,1);画布的高(0,200,400)转换成坐标(1,0,-1);
            // 首先:获取画布宽高,除以2,得到原点到边框的距离,也就是一半的宽和高。
            // 其次:获取点击后得到的宽 减去 一半的宽,再除以一半的宽。
            // 最后:一半的高 减去 获取点击后得到的高,再除以一半的高。
            const halfWidth = ctx.offsetWidth / 2;
            const halfHeigth = ctx.offsetHeight / 2;
            const clickX = (domx - halfWidth) / halfWidth;
            const clickY = (halfHeigth - domy) / halfHeigth;
            console.log(clickX, clickY);
            // 4.使用坐标在画布上绘制点
            // gl.vertexAttrib2f(aPosition, clickX, clickY) // 两个坐标点就用vertexAttrib2f
            // gl.drawArrays(gl.POINTS, 0, 1);
            // 5.绘制多的点
            points.push({ clickX, clickY })
            points.forEach(element => {
                gl.vertexAttrib2f(aPosition, element.clickX, element.clickY) // 两个坐标点就用vertexAttrib2f
                gl.drawArrays(gl.POINTS, 0, 1);
            });
        }
    </script>
</body>

</html>

6. 效果如下

相关推荐
子兮曰25 分钟前
Agency-Agents 深度解析:400+ AI 专家的"梦之队"如何重塑开发工作流
前端·后端·vibecoding
山河木马1 小时前
渲染管线-计算得到gl_Position(顶点着色器)之后续GPU流程
javascript·webgl·图形学
竹林8181 小时前
用 The Graph 查询链上数据实战:从手搓 RPC 到 Subgraph,我的 NFT 项目数据加载快了 10 倍
前端·javascript
妙码生花1 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十九):点选验证码代码逐行目检
前端·后端·go
Awu12272 小时前
⚡从零开发 Agent CLI(五)实现一个可治理、可扩展的工具系统
前端·人工智能·claude
咪库咪库咪3 小时前
Vue3-生命周期
前端
莪_幻尘3 小时前
你的 AI Skill 越多越蠢?Token 上下文爆炸的求生指南
前端·ai编程
lichenyang4533 小时前
从 has.echo 到异步 API 注册表:一次 ASCF API 回调不触发的排查复盘
前端
林瞅瞅4 小时前
Nuxt3 项目部署 Nginx 防盗链后特定 JS 文件 403 问题修复方案
前端
kyriewen4 小时前
别再每次都 Google 了:我整理了前端日常最常踩的 10 个 Git 坑,附速查表
前端·javascript·git