前言

在上篇文章中,我们使用WebGL实现了网格背景,当时有提到说使用WebGL来实现的好处之一,是网格背景可以与画布上的其他元素更好地融合,比如一起缩放平移,那么在WebGL中怎么实现缩放和平移呢?现在我们已经实现了网格背景,接下来我们就用网格背景作为例子来了解一下WebGL中的缩放和平移。

这里需要用到我之前学过的变换矩阵,不熟悉变换矩阵的小伙伴可以看我之前的文章《CSS transform与仿射变换》,或者查阅其他相关资料;简单来说就是使用矩阵来表示顶点坐标的变换。

具体实现

接下来我们在代码中来具体实现这个效果。

1. 改造顶点着色器

首先我们先改造顶点着色器的GLSL代码,对顶点应用变换矩阵。

attribute vec2 a_vertexPosition;
attribute vec2 uv; varying vec2 vUv; uniform int scale;
uniform vec2 offset; mat3 translateMatrix = mat3( // 平移矩阵
1.0, 0.0, 0.0, // 第一列
0.0, 1.0, 0.0, // 第二列
offset.x, offset.y, 1.0 // 第三列
); mat3 scaleMatrix = mat3( // 缩放矩阵
float(scale), 0.0, 0.0,
0.0, float(scale), 0.0,
0.0, 0.0, 1.0
); void main() {
gl_PointSize = 1.0;
vUv = uv;
vec3 pos = scaleMatrix * translateMatrix * vec3(a_vertexPosition, 1.0);
gl_Position = vec4(pos, 1.0);
}

代码中我们增加接收两个uniform常量scaleoffset,表示缩放比例和平移的偏移量;并且定义两个矩阵:平移矩阵translateMatrix和缩放矩阵scaleMatrix。需要注意GLSL这里矩阵的写法是列主序的,也就是说这里的第一行其实是平常我们认为的第一列,也就是说假如有平移矩阵乘以原坐标:translateMatrix * vec3(a_vertexPosition, 1.0),我们会得到如下结果:

\[\begin{bmatrix}
1 & 0 & offset.x \\
0 & 1 & offset.y \\
0 & 0 & 1
\end{bmatrix} \times
\begin{bmatrix}
x_0 \\
y_0 \\
1
\end{bmatrix} =
\begin{bmatrix}
x_0 + offset.x\\
y_0 + offset.y \\
1
\end{bmatrix}
\]

将两个矩阵相乘表示叠加效果,再与坐标向量相乘后,我们就能得到映射的新顶点坐标。

2. 改造js代码

顶点着色器中增加接收的两个参数需要js代码传递过去,接下来我们就对js代码进行改造。

首先是设置这两个变量的初始值,scale等于1表示原始大小,offset设置的初始偏移量为0,也就是不偏移。

// ...
renderer.uniforms.scale = 1;
renderer.uniforms.offset = [0.0, 0.0];
// ...

然后我们添加对鼠标滚轮事件以及鼠标按下和放开事件的监听。

// ...
const addEvent = () => {
patternPracticeRef.value.addEventListener('mousewheel', wheelEventHandler);
patternPracticeRef.value.addEventListener('mouseup', mouseUpHandler);
patternPracticeRef.value.addEventListener('mousedown', mouseDownHandler);
}
addEvent();
// ...

接下来我们就来完成这三个事件监听的回调函数。

我们先来完成滚轮事件的监听回调,这个比较简单。e.wheelDeltaY > 0表示滚轮向下滚动,实现放大效果,这里我使用了整数表示放大倍数,这是因为之前我有使用浮点数来实现过,但是存在一些问题,比如绘制出来的网格线会有可能粗细不同,甚至会缺少一些线,这估计是由于精度原因导致的,我还没去进一步的研究。

const wheelEventHandler = e => {
e.preventDefault();
if (e.wheelDeltaY > 0) { // 向下滚,放大
if (renderer.uniforms.scale <= 50) {
renderer.uniforms.scale += 1;
}
} else { // 向上滚,缩小
if (renderer.uniforms.scale > 1) {
renderer.uniforms.scale -= 1;
}
}
}
const mouseDownHandler = e => {
// ...
}
const mouseUpHandler = e => {
// ...
}

此时我们在页面上进行测试一下,就可以看到缩放的效果实现了。

接着我们来实现平移的效果,因为偏移量需要根据鼠标的初始位置和移动结束的位置计算得到,所以我们增加一个对象类型的变量lastPos来记录鼠标的初始位置,并在鼠标按下的事件回调中对它赋值。

// ...
const lastPos = {};
// ...
const mouseDownHandler = e => {
e.preventDefault();
// 记录初始位置
lastPos.x = e.offsetX;
lastPos.y = e.offsetY;
// 绑定事件
patternPracticeRef.value.addEventListener('mousemove', mouseMoveHandler);
}

在鼠标按下后,我们对鼠标的移动事件开启监听,便于实时更新位置信息。

const mouseMoveHandler = e => {
e.preventDefault();
// 计算坐标差值并转换为Canvas差值
const { offsetX: x, offsetY: y } = e;
const translateX = (x - lastPos.x) / patternPracticeRef.value.width;
const translateY = (lastPos.y - y) / patternPracticeRef.value.height;
// 设置偏移量
renderer.uniforms.offset = [translateX, translateY];
}

因为在页面上y轴向下,与WebGL中y轴向上是相反的,所以这里计算y方向的偏移量使用lastPos.y - y。我们通过除以canvas的宽高,就能转换得到偏移量在WebGL里的对应数值。

接着我们在鼠标按键松开事件监听的回调中,对移动监听进行移除。

const mouseDownHandler = e => {
e.preventDefault();
// 解绑事件
patternPracticeRef.value.addEventListener('mousemove', mouseMoveHandler);
}

此时在页面上测试,能看到我们可以对网格进行拖动平移了,但很明显这其中还存在问题,当我们完成第一次平移后,想再点击鼠标来平移,就会发现在按下鼠标的那一刻网格会发生闪烁,这是因为在上一次平移完成后,图片的中心点已经改变了,而在GLSL代码中的偏移矩阵中,偏移量的设置是相对(0, 0)点的,所以会闪烁一下,也就是说我们需要考虑中心点的影响,在第二次平移时要在新的中心点的基础上去进行平移,在这里我使用一个lastCenter变量来存储中心点的位置。

const lastPos = {}, lastCenter = {};

并在鼠标按键松开的事件回调中对画布中心点的信息进行更新。

const mouseUpHandler = e => {
e.preventDefault();
// 计算坐标差值并转换为Canvas差值
const { offsetX: x, offsetY: y } = e;
const translateX = (x - lastPos.x) / patternPracticeRef.value.width;
const translateY = (lastPos.y - y) / patternPracticeRef.value.height;
// 更新新的中心点
lastCenter.x = translateX + lastCenter.x;
lastCenter.y = translateY + lastCenter.y;
// 解除事件绑定
patternPracticeRef.value.removeEventListener('mousemove', mouseMoveHandler);
}

同时在鼠标移动过程中偏移量的更新我们也要把中心点的坐标考虑进去。

const mouseMoveHandler = e => {
e.preventDefault();
// 计算坐标差值并转换为Canvas差值
const { offsetX: x, offsetY: y } = e;
const translateX = (x - lastPos.x) / patternPracticeRef.value.width;
const translateY = (lastPos.y - y) / patternPracticeRef.value.height;
// 设置偏移量
renderer.uniforms.offset = [translateX + lastCenter.x, translateY + lastCenter.y];
}

这个时候我们再去测试一下,可以看到就是正常的了。

总结

到这里为止我们就实现了在WebGL中的缩放和平移,这里主要就是借助了鼠标事件的监听和变换矩阵的应用。

完整代码

最终效果

可视化学习:WebGL实现缩放平移的更多相关文章

  1. Android实现支持缩放平移图片

    本文主要用到了以下知识点 Matrix GestureDetector 能够捕捉到长按.双击 ScaleGestureDetector 用于检测缩放的手势 自由的缩放 需求:当图片加载时,将图片在屏幕 ...

  2. Android 手势检测实战 打造支持缩放平移的图片预览效果(下)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39480503,本文出自:[张鸿洋的博客] 上一篇已经带大家实现了自由的放大缩小图 ...

  3. Tensorflow学习笔记3:TensorBoard可视化学习

    TensorBoard简介 Tensorflow发布包中提供了TensorBoard,用于展示Tensorflow任务在计算过程中的Graph.定量指标图以及附加数据.大致的效果如下所示, Tenso ...

  4. CSGrandeur的WebGL学习——WebGL教程

    在线查看:http://csgrandeur.gitbooks.io/webgl-learn/content/ 离线mobi:http://files.cnblogs.com/files/CSGran ...

  5. Android绘画板(普通绘画模式和缩放平移绘画模式)

    ScaleSketchPadDemo 项目地址: demo apk体验下载 demo2 apk体验下载 用法: 进入项目根目录:https://github.com/ShaunSheep/ScaleS ...

  6. Android 开源可缩放平移的绘画板

    ScaleSketchPadDemo 此项目包含两个模块 app1 为普通绘画板 app2 为可所发的绘画板 方便各位Android 开发者理解和使用 用法: 进入项目根目录:https://gith ...

  7. 可视化学习Tensorboard

    可视化学习Tensorboard TensorBoard 涉及到的运算,通常是在训练庞大的深度神经网络中出现的复杂而又难以理解的运算.为了更方便 TensorFlow 程序的理解.调试与优化,发布了一 ...

  8. R语言可视化学习笔记之添加p-value和显著性标记

    R语言可视化学习笔记之添加p-value和显著性标记 http://www.jianshu.com/p/b7274afff14f?from=timeline   上篇文章中提了一下如何通过ggpubr ...

  9. C#图片缩放平移 —— 从功能分析到编码实现

    序 一直都是在看别人的博客,查到想要的,看完后把页面一关就万事大吉了,没啥感觉:直到后来遇到了同样的问题,总想不起来咋弄,关键是还查不到以前看过的,郁闷!现在想想,还是“好记性不如烂笔头”啊,自己弄过 ...

  10. 软件项目技术点(1)——d3.interpolateZoom-在两个点之间平滑地缩放平移

    AxeSlide软件项目梳理   canvas绘图系列知识点整理 软件参考d3的知识点 我们在软件中主要用到d3.js的核心函数d3.interpolateZoom - 在两个点之间平滑地缩放平移.请 ...

随机推荐

  1. [JVM] Java内存分配

    Java内存分配 程序计数器 程序计数器是一块较小的内存区域,作用可以看做是当前线程执行的字节码的位置指示器.分支.循环.跳转.异常处理和线程恢复等基础功能都需要依赖这个计算器来完成. 虚拟机栈 虚拟 ...

  2. 【解决方案】Java 互联网项目如何防止集合堆内存溢出(一)

    目录 前言 一.代码优化 1.1Stream 流自分页 1.2数据库分页 1.3其它思考 二.硬件配置 2.1云服务器配置 三.文章小结 前言 OOM 几乎是笔者工作中遇到的线上 bug 中最常见的, ...

  3. Swoole从入门到入土(7)——TCP服务器[大杂烩]

    这一篇是异步风格的TCP服务器的最后一篇,主要的目的是疏理之前几篇没提到的一些比较有用的属性.配置.函数与事件,当然不是全部.如果想知道全部,请查看官网. 1.属性 Swoole\Server-> ...

  4. 易语言读取Mysql表数据

    源码下载: https://download.csdn.net/download/IndexMan/12029860 1.界面设计 2.效果展示 3.源码展示 程序集变量: 读取数据按钮: 读取数据子 ...

  5. win32-读取控制台中所有的字符串

    我们可以先读取字符串所占的行数,再乘以控制台的实际宽度 bool ReadFromConsole() { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDL ...

  6. [Android 逆向]frida 破解 切水果大战原版.apk

    1. 手机安装该apk,运行,点击右上角礼物 提示 支付失败,请稍后重试 2. apk拖入到jadx中,待加载完毕后,搜素失败,找到疑似目标类MymmPay的关键方法payResultFalse 4. ...

  7. 一个自定义可扩展的检测变量的函数typeofIt();

    自定义方法typeofIt()是用来判断传入的变量或属性是什么类型的; 1.如果是基础类型变量则返回代表基础变量类型小写格式的字符串及一些简易说明; 2.如果是对象类型变量则返回结尾带有"O ...

  8. 利用wiile双层循环打印各种星星---day06

    # 十行十列小星星 j = 0 #定义行数 while j<10: #当行数小于10的时候 i=0 #定义列 while i <10: #当列小于10的时候 print('*',end=' ...

  9. 【LeetCode剑指offer 02】矩阵中的路径(老鼠走迷宫plus,应用深度优先搜索与回溯机制)

    矩阵中的路径 https://leetcode.cn/problems/ju-zhen-zhong-de-lu-jing-lcof/ 给定一个 m x n 二维字符网格 board 和一个字符串单词 ...

  10. DataGear 制作折柱图数据可视化图表

    利用 DataGear  看板的 自定义图表选项 功能,可以很方便地制作折柱图(或者折线-饼图.柱状-饼图)数据可视化图表. 假设有如下CSV数据集,包括名称和两个指标值数据: 名称, 指标0, 指标 ...