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. 效果如下

相关推荐
程序员林北北几秒前
【前端进阶之旅】50 道前端超难面试题(2026 最新版)|覆盖 HTML/CSS/JS/Vue/React/TS/ 工程化 / 网络 / 跨端
前端·javascript·css·vue.js·html
糕冷小美n8 小时前
elementuivue2表格不覆盖整个表格添加固定属性
前端·javascript·elementui
小哥不太逍遥8 小时前
Technical Report 2024
java·服务器·前端
沐墨染9 小时前
黑词分析与可疑对话挖掘组件的设计与实现
前端·elementui·数据挖掘·数据分析·vue·visual studio code
anOnion9 小时前
构建无障碍组件之Disclosure Pattern
前端·html·交互设计
threerocks9 小时前
前端将死,Agent 永生
前端·人工智能·ai编程
问道飞鱼10 小时前
【前端知识】Vite用法从入门到实战
前端·vite·项目构建
爱上妖精的尾巴10 小时前
8-10 WPS JSA 正则表达式:贪婪匹配
服务器·前端·javascript·正则表达式·wps·jsa
shadow fish11 小时前
react学习记录(三)
javascript·学习·react.js
小疙瘩11 小时前
element-ui 中 el-upload 多文件一次性上传的实现
javascript·vue.js·ui