整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

WebGL着色器简明教程「2022」

WebGL着色器简明教程「2022」

本文中,我们将了解如何使用超过 150 行代码将图像渲染到页面!我知道可以只使用一个<img>标签并完成它。但这样做是一个很好的练习,因为它迫使我们引入许多重要的 WebGL 概念。

我最近在一个需要使用 WebGL 的项目上工作。我试图在浏览器中的地图上渲染数千个多边形,但结果证明 GeoJSON 太慢了。为了加快速度,我想尽可能降低到最低水平,并使用 WebGL 和着色器实际编写可以直接在 GPU 上运行的代码。我一直想了解着色器,但一直没有机会,所以这是一个在解决非常具体的技术挑战的同时学习新东西的好机会。

起初,我很难弄清楚我需要做什么。复制和粘贴示例代码通常不起作用,而且我并没有真正了解如何从示例转到我需要的自定义解决方案。然而,一旦我完全理解了这一切是如何结合在一起的,它突然在我脑海中响起,结果证明解决方案非常简单。最困难的部分是围绕一些概念思考。所以,我想写一篇文章解释我学到了什么,帮助你理解这些概念,并希望让你更容易编写你的第一个着色器。

以下是我们将在本文中执行的操作:

  1. 我们将编写两个着色器程序,告诉 GPU 如何将坐标列表转换为屏幕上的彩色三角形。
  2. 我们将向着色器传递一个坐标列表,告诉它在屏幕的何处绘制三角形。
  3. 我们将创建一个“图像纹理”,将图像上传到 GPU,以便它可以将其绘制到三角形上。
  4. 我们将给着色器一个不同的坐标列表,以便它知道每个三角形内的图像像素。

希望你可以使用这些概念作为起点,使用 WebGL 做一些非常酷且有用的事情。

即使你最终使用库来帮助编写 WebGL 代码,我发现了解幕后的原始 API 调用对于了解实际发生的情况很有用,尤其是在出现问题时。

1、WebGL 入门

要在浏览器中使用 WebGL,你需要向<canvas>页面添加标签。使用画布,你可以使用 2D Canvas API 进行绘制,也可以选择使用 3D WebGL API版本 1 或 2。我实际上并不了解 WebGL 1 和 2 之间的区别,但我会希望有一天能了解更多。我将在这里讨论的代码和概念适用于这两个版本。

如果想让你的画布填充视口,你可以从这个简单的 HTML 开始:

<!doctype html>
<html lang="en">
    <meta charset="UTF-8">
    <title>WebGL</title>
    <style>
        html, body, canvas {
            width: 100%;
            height: 100%;
            border: 0;
            padding: 0;
            margin: 0;
            position: absolute;
        }
    </style>
    <body>
        <canvas></canvas>
        <script></script>
    </body>
</html>

这会给你一个空白的、白色的、无用的页面。你需要一些 JavaScript 来实现它。在<script>标签内,添加这些行以访问画布的 WebGL API:

const canvas=document.querySelector('canvas');
const gl=canvas.getContext('webgl');

2、编写第一个 WebGL 着色器程序

WebGL 基于 OpenGL,并使用相同的着色器语言。没错,着色器程序(Shader)是用他们自己的语言 GLSL 编写的,它代表图形库着色器语言。

GLSL 让我想起了 C 或 JavaScript,但它有自己的特点,局限性非常大,但也非常强大。很酷的一点是,它直接在 GPU 上而不是在 CPU 上运行。因此它可以非常快速地完成普通 CPU 程序无法完成的事情。它针对使用向量和矩阵处理数学运算进行了优化。

我们需要两种类型的着色器:顶点(Vertex)着色器和片段(Fragment)着色器。顶点着色器可以进行计算以确定每个顶点(三角形的角)的位置。片段着色器计算出如何为三角形内的每个片段(像素)着色。

这两个着色器相似,但在不同的时间做不同的事情。顶点着色器首先运行,以确定每个三角形的去向,然后它可以将一些信息传递给片段着色器,因此片段着色器可以计算出如何绘制每个三角形。

3、你好,顶点着色器的世界!

这是一个基本的顶点着色器,它将接收一个带有 x,y 坐标的向量。向量基本上只是一个具有固定长度的数组。vec2是有 2 个数字的数组,vec4是有 4 个数字的数组。所以,这个程序将采用一个全局“属性”变量,一个名为“points”的 vec2(这是我编的一个名字)。

然后它会告诉 GPU,这正是顶点将要去的地方,方法是将它分配给另一个内置于 GLSL 中的名为gl_Position 的变量。

它将针对三角形的每个顶点运行,并且每次points都有不同的 x,y 值。稍后你将看到我们如何定义和传递这些坐标。

这是我们的第一个“你好,世界!” 顶点着色器程序:

attribute vec2 points;

void main(void) {
    gl_Position=vec4(points, 0.0, 1.0);
}

此处不涉及任何计算,只是我们需要将 vec2 转换为 vec4。前两个数字是 x 和 y,第三个是 z,我们只需将其设置为 0.0,因为我们正在绘制二维图片,我们不需要担心第三维。我不知道第四个值是什么意思,但我们只是将其设置为 1.0。根据我的阅读,我认为这与使矩阵数学更容易有关。

我喜欢 GLSL 中的这一点,向量是一种基本数据类型,你可以使用其他向量轻松创建向量。我们可以这样写上面的行:

gl_Position=vec4(points[0], points[1], 0.0, 1.0);

但相反,我们能够使用快捷方式,只需将 vec2 点作为第一个参数传入,GLSL 就知道该怎么做。它让我想起了在 JavaScript 中使用扩展运算符:

// javascript
gl_Position=[...points, 0.0, 1.0];

因此,如果角形角之一的 x 为 0.2,y 为 0.3,我们的代码将有效地执行以下操作:

gl_Position=vec4(0.2, 0.3, 0.0, 1.0);

但是我们不能像这样将 x 和 y 坐标硬编码到我们的程序中,否则所有的三角形都只是屏幕上的一个点。我们使用属性向量(Attribute)代替,以便每个角(或顶点)可以位于不同的位置。

4、使用片段着色器

顶点着色器为每个三角形的每个角运行一次,而片段着色器为每个三角形内的每个彩色像素运行一次。

顶点着色器使用名为 gl_Position的全局 vec4 变量定义每个顶点的位置,而片段着色器通过使用名为gl_FragColor 的局 vec4 变量定义每个像素的颜色。以下是我们如何用红色像素填充所有三角形:

void main() {
    gl_FragColor=vec4(1.0, 0.0, 0.0, 1.0);
}

这里颜色向量是 RGBA,因此红色、绿色、蓝色和 alpha 中的每一个都是介于 0 和 1 之间的数字。所以上面的例子只是将每个片段或像素设置为完全不透明的亮红色。

5、访问着色器中的图像

你通常不会用纯色填充所有三角形,因此,我们希望片段着色器引用图像(或“纹理”)并为三角形内的每个像素提取正确的颜色。

我们需要使用颜色信息访问纹理,以及一些告诉我们图像如何映射到形状上的“纹理坐标”。

首先,我们将修改顶点着色器以访问坐标并将它们传递给片段着色器:

attribute vec2 points;
attribute vec2 texture_coordinate;

varying highp vec2 v_texture_coordinate;

void main(void) {
    gl_Position=vec4(points, 0.0, 1.0);
    v_texture_coordinate=texture_coordinate;
}

如果你像我一样,可能会担心会需要各种疯狂的三角函数,但别担心 - 事实证明这是最简单的部分,这要归功于 GPU 的魔力。

我们为每个顶点获取一个纹理坐标,然后将其传递给变量中的片段着色器,该varying变量将“插入”每个片段或像素的坐标。这本质上是两个维度的百分比,因此对于三角形内的任何特定像素,我们将准确知道要选择图像的哪个像素。

图像存储在一个名为 sampler的二维采样器变量中。我们从顶点着色器接收varying纹理坐标,并使用GLSL 函数texture2D从纹理中采样适当的单个像素。

这听起来很复杂,但由于 GPU 的魔力,它变得非常简单。我们需要做任何数学运算的唯一部分是将三角形的每个顶点坐标与图像的坐标相关联,稍后我们将看到它变得非常简单。

precision highp float;
varying highp vec2 v_texture_coordinate;
uniform sampler2D sampler;

void main() {
    gl_FragColor=texture2D(sampler, v_texture_coordinate);
}

6、编译着色器程序

我们刚刚研究了如何使用 GLSL 编写两个不同的着色器,但我们还没有讨论过如何在 JavaScript 中做到这一点。只需要将这些 GLSL 着色器转换为 JavaScript 字符串,然后我们就可以使用 WebGL API 编译它们并将它们放在 GPU 上。

有些人喜欢把shader源代码直接放在HTML中使用<script type="x-shader/x-vertex">之类的script标签,然后用.innerText把代码拉出来。你还可以将着色器放入单独的文本文件中并使用fetch 加载 。具体怎么做取决于你。

我发现直接在 JavaScript 中使用模板字符串编写着色器源代码是最简单的。看起来是这样的:

const vertexShaderSource=`
    attribute vec2 points;
    attribute vec2 texture_coordinate;

    varying highp vec2 v_texture_coordinate;

    void main(void) {
        gl_Position=vec4(points, 0.0, 1.0);
        v_texture_coordinate=texture_coordinate;
    }
`;

const fragmentShaderSource=`
    precision highp float;
    varying highp vec2 v_texture_coordinate;
    uniform sampler2D sampler;

    void main() {
        gl_FragColor=texture2D(sampler, v_texture_coordinate);
    }
`;

接下来,我们需要创建一个 GL“程序”并将这两个不同的着色器添加到其中,如下所示:

// create a program (which we'll access later)
const program=gl.createProgram();

// create a new vertex shader and a fragment shader
const vertexShader=gl.createShader(gl.VERTEX_SHADER);
const fragmentShader=gl.createShader(gl.FRAGMENT_SHADER);

// specify the source code for the shaders using those strings
gl.shaderSource(vertexShader, vertexShaderSource);
gl.shaderSource(fragmentShader, fragmentShaderSource);

// compile the shaders
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);

// attach the two shaders to the program
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);

最后,我们必须告诉 GL 链接并使用我们刚刚创建的程序。请注意,一次只能使用一个程序:

gl.linkProgram(program);
gl.useProgram(program);

如果程序出现问题,我们应该将错误记录到控制台。否则,它将默默地失败:

if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    console.error(gl.getProgramInfoLog(program));
}

如您所见,WebGL API 非常冗长。但是,如果仔细查看这些代码,会发现它们并没有做任何太令人惊讶的事情。这些代码块非常适合复制和粘贴,因为很难记住它们,而且它们很少更改。你可能需要更改的唯一部分是模板字符串中的着色器源代码。

7、绘制三角形

现在我们的程序已经全部连接好,是时候给它一些坐标并让它在屏幕上绘制一些三角形了!

首先,我们需要了解 WebGL 的默认坐标系。它与屏幕上的常规像素坐标系完全不同。在 WebGL 中,画布的中心是 0,0,左上角是 -1,-1,右下角是 1,1。

如果我们想渲染一张照片,需要一个矩形。但是 WebGL 只知道如何绘制三角形。那么我们如何使用三角形绘制一个矩形呢?我们可以使用两个三角形来创建一个矩形。我们将有一个三角形覆盖左上角,另一个覆盖右下角,如下所示:

要绘制三角形,需要指定每个三角形三个角的坐标。让我们创建一个数字数组。两个三角形的 x 和 y 坐标都将在一个数组中,如下所示:

const points=[
    // first triangle
    // top left
    -1, -1,

    // top right
    1, -1,

    // bottom left
    -1, 1,

    // second triangle
    // bottom right
    1, 1,

    // top right
    1, -1,

    // bottom left
    -1, 1,
];

要将数字列表传递到我们的着色器程序中,我们必须创建一个“缓冲区”,然后将一个数组加载到缓冲区中,然后告诉 WebGL 将缓冲区中的数据用于我们的着色器程序中的属性。

我们不能只将 JavaScript 数组加载到 GPU 中,它必须是严格类型的。所以我们把它包装在一个Float32Array 中。 我们也可以使用整数或任何对我们的数据有意义的类型,但对于坐标,浮点数最有意义。

// create a buffer
const pointsBuffer=gl.createBuffer();

// activate the buffer, and specify that it contains an array
gl.bindBuffer(gl.ARRAY_BUFFER, pointsBuffer);

// upload the points array to the active buffer
// gl.STATIC_DRAW tells the GPU this data won't change
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW);

请记住,我在着色器程序的顶部创建了一个名为“points”的属性,带有attribute vec2 points;? 现在我们的数据在缓冲区中,并且缓冲区处于活动状态,我们可以用需要的坐标填充那个“points”属性:

// get the location of our "points" attribute in our shader program
const pointsLocation=gl.getAttribLocation(program, 'points');

// pull out pairs of float numbers from the active buffer
// each pair is a vertex that will be available in our vertex shader
gl.vertexAttribPointer(pointsLocation, 2, gl.FLOAT, false, 0, 0);

// enable the attribute in the program
gl.enableVertexAttribArray(pointsLocation);

8、将图像加载到纹理中

在 WebGL 中,纹理是一种在网格中提供大量数据的方法,这些数据可用于将像素绘制到形状上。图像是一个明显的例子,它们是沿行和列的红色、蓝色、绿色和 alpha 值的网格。但是,你可以将纹理用于根本不是图像的事物。就像计算机中的所有信息一样,它最终只是数字列表。

由于我们在浏览器中,我们可以使用常规的 JavaScript 代码来加载图像。加载图像后,我们将使用它来填充纹理。

在我们执行任何 WebGL 代码之前先加载图像可能是最简单的,然后在图像加载后运行整个 WebGL 初始化的东西,所以我们不需要等待任何东西,像这样:

const img=new Image();
img.src='photo.jpg';
img.onload=()=> {
    // assume this runs all the code we've been writing so far
    initializeWebGLStuff();
};

现在我们的图像已经加载,我们可以创建一个纹理并将图像数据上传到其中。

// create a new texture
const texture=gl.createTexture();

// specify that our texture is 2-dimensional
gl.bindTexture(gl.TEXTURE_2D, texture);

// upload the 2D image (img) and specify that it contains RGBA data
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);

由于我们的图像可能不是2的N次幂,因此还必须告诉 WebGL 在放大或缩小图像时如何选择要绘制的像素,否则会抛出错误。

// tell WebGL how to choose pixels when drawing our non-square image
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

// bind this texture to texture #0
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);

最后,我们想在着色器程序中访问这个纹理。我们用代码uniform sampler2D sampler;定义了一个二维 uniform变量,告诉 GPU 应该使用我们的新纹理。

// use the texture for the uniform in our program called "sampler",
gl.uniform1i(gl.getUniformLocation(program, 'sampler'), 0);

9、使用纹理坐标绘制带有图像的三角形

我们快完成了!下一步非常重要。我们需要告诉着色器我们的图像应该如何绘制到三角形上。我们希望将图像的左上角绘制在左上三角形的左上角。等等。

图像纹理的坐标系与我们使用的三角形不同,所以我们必须考虑一下,不幸的是不能只使用完全相同的坐标。以下是它们的不同之处:

纹理坐标应该与我们的三角形顶点坐标的顺序完全相同,因为这就是它们在顶点着色器中一起显示的方式。当我们的顶点着色器为每个顶点运行时,它还能够访问每个纹理坐标,并将其作为varying变量传递给片段着色器。

我们将使用与上传三角坐标数组几乎相同的代码,只是现在我们将把它与名为“texture_coordinate”的属性相关联。

const textureCoordinates=[
    // first triangle
    // top left
    0, 1,

    // top right
    1, 1,

    // bottom left
    0, 0,

    // second triangle
    // bottom right
    1, 0,

    // top right
    1, 1,

    // bottom left
    0, 0,
];

// same stuff we did earlier, but passing different numbers
const textureCoordinateBuffer=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordinateBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates), gl.STATIC_DRAW);

// and associating it with a different attribute
const textureCoordinateLocation=gl.getAttribLocation(program, 'texture_coordinate');
gl.vertexAttribPointer(textureCoordinateLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(textureCoordinateLocation);

10、最后一步!绘制三角形

现在我们已经将着色器、所有坐标和图像加载到 GPU 中,我们已经准备好实际运行着色器程序并让它将我们的图像绘制到画布上。

为此,我们只需要一行代码:

gl.drawArrays(gl.TRIANGLES, 0, 6);

这告诉 WebGL 使用我们的点数组和纹理坐标数组来绘制三角形。这里的数字6意味着我们数组中的每 6 个数字定义一个三角形。每个三角形都有 3 个角,每个角(或顶点)都有一个 x 和 y 坐标。

11、结束语

使用 GPU 绘制图像需要学习许多不同的东西,我发现这存在一个陡峭的学习曲线,但是一旦了解着色器的实际作用、纹理是什么、如何为着色器提供一些数字列表以及它们如何组合在一起,它就开始变得有意义了,并且我意识到这一切有多么强大。

我希望你已经能够看到一些简单和强大的功能。我知道 WebGL API 可能非常冗长,而且我仍然不能完全确定每个函数的作用,这对我来说绝对是一种新的编程范式,因为 GPU 与 CPU 如此不同,但这就是它如此令人兴奋的原因。


原文链接:http://www.bimant.com/blog/webgl-shader-crash-course/

荐12个曾经让我眼界大开的网站,每一个都像发现了新大陆!要么超级好玩,要么超级实用(能帮你省钱的那种)!相信总有一个你看了就会立马爱上!


1:正版中国 —— 以较低的价格买到正版软件

网址:https://getitfree.cn/

正版中国是一个分享正版软件限时免费信息的网站。像常见的Office软件给的折扣力度都很大,有时候直接到了2折,真的能省不少钱!


从网站的分类信息中我们可以看到,几乎包含了不同系统的软件产品,比如PC、Mac、iOS、Android、UMP……



2:考拉新媒体导航 —— 新媒体人必备的好网站!

网址:https://www.kaolamedia.com/

作为新媒体人,无论是做内容运营、活动运营,还是用户运营,工作中经常需要用到不少网站。

前几天,我发现了一个超赞的新媒体导航网站,里面搜集了100+个新媒体人常用的效率工具!!

涵盖:配图网站、自媒体网站、公众号辅助工具、数据分析工具、裂变工具、社群运营工具、表单工具、创意H5工具、小程序工具、视频剪辑、效率管理等…

简直太用心了!我觉得自己可以清理一波收藏夹了……


3:美丽化学 —— 可能会让你疯狂地爱上化学

网址:http://www.envisioningchemistry.cn

说到化学,很多人的反应就是充满了危险,或者是难闻的气味。但也有很多人就是喜欢化学反应的奇妙,甚至还专门建立了一个网站,从微观的角度展示神奇的化学世界。


比如这个硝酸钾和氯化铵的结晶过程 ,在微观世界实在是太美了!

还有初中学化学时非常熟悉的沉淀反应:


还有这个双氧水的催化反应实验:


【美丽化学】的项目发起人是梁琰,本硕毕业于清华大学化学系,他曾在一席的舞台上分享过自己这个项目背后的故事,大家感兴趣的话也可以去看一看。

一席 | 演讲

4:Office-converter —— 可能是最好用的在线格式转换网站

网址:https://cn.office-converter.com/说到格式转换,相信很多人都是头疼不已,比如PDF转Word?flv格式的视频如何转换成mp4格式?使用这个万能的在线格式转换网站就好啦~

无须下载专门的软件,在线就可以免费转换各种类型的文件格式,包括:文档、视频、音频、图片、电子书等。






5:AlteredQualia —— 各种好玩的神器~

网址:https://alteredqualia.com/

这是一位WebGL大神的个人主页,里面有各种好玩的工具。

什么是WebGL?简单来说,就是在无需安装任何硬件的情况下,我们仅通过浏览器就能查看各种3D影像的技术。

部分截图

每一个都非常有意思。

比如下面的星球放射线工具 。截下来作为PPT的背景图片也是非常好的素材

https://alteredqualia.com/three/examples/lines_sphere_gl.html

又比如这个会随着鼠标移动同步转动眼球的爱因斯坦

https://alteredqualia.com/xg/examples/albert.html

还有这个有点魔性的江南style舞蹈

https://alteredqualia.com/three/examples/webgl_psy.html

这里就只为大家展示这3个小工具,如果你喜欢,不妨把大神的每个WebGL工具都玩一遍~


6:求字体网 —— 轻松识别不认识的字体

网址:http://new.qiuziti.com/

遇到喜欢但又不认识的字体怎么办?别着急,给文字拍张照片,或截个图,上传到【求字体网】,就可以识别出来了

比如这是@这是三金 做的一张读书笔记PPT


我想知道标题用了什么字体,就可以这么做:

Step 1:截取标题字体部分

页面太复杂的话,网站识别效果较差,所以我们只截取标题区域。

Step 2:上传图片

Step 3:选中清晰且有特点的单字

根据识别结果,发现「方正喵呜体」最符合。另外,发现没?网站还贴心地给出了字体的商用情况以及官方下载链接,可以说很贴心了

类似网站:识字体网


7:51PPT模板 —— 集结各路PPT大神的模板作品

网址:http://www.51pptmoban.com/ppt/

性质:全部免费;质量高

这个网站是由个人运营的,站长会向每个PPT作品的原作者去申请授权,然后整理发布。该网站不仅仅提供免费的PPT模板,还有很多PPT教程、PPT图表等资源!


下载方法:

? 选择你想要的模板

? 往下拉,找到「下载地址」按钮


? 最后点击「本地下载」。这样模板就下好了,全程无须注册~


来看一下模板的质量:




8:OfficePlus —— 微软官方出品的免费模板下载网站

网址:http://www.officeplus.cn/Template/Home.shtml性质:全部免费;质量高这是微软Office官方网站,这个网站吧不光有PPT模板,还有Word、Excel模板,甚至还有很多高质量的图片。


里面的模板质量有多高呢?我放两份给大家看看:



直接秒杀很多付费模板呀!这样的PPT模板OfficePlus上还有很多。

另外呢,Office既然作为微软自家孩子,自然有特殊福利。假如你的软件为Office365,那么在新建PPT的时候,直接搜索关键词,比如 “ 报告 ”,是可以直接搜索到OfficePLUS上的模板。



9:OpenStax CNX —— 免费分享教科书的网站

网址:https://cnx.org/

看到满屏的英文是不是慌啦?其实只要利用网页的翻译功能翻成中文就好。


这是莱斯大学(Rice University) 的巴拉纽克博士( Dr. Richard Baraniuk)在1999年创办的一个网站,分享的书籍涉及设计、艺术、商业、数学计算等领域。


所有书籍会提供详细的目录 ↓


下载的时候也无须注册,而且免费。可以说是非常良心了。


所以,如果你想找国外的教科书,不妨先来这个网站试试。


10:tunefind —— 快速找到热门影视剧的BGM!

网址:https://www.tunefind.com/movie/zootopia-2016

不知道你有没有这样的经历?看到一部好看的片子(电影、美剧、综艺都行)或者一款好玩的游戏,经常被里面的BGM吸引,每次为了找到歌名,恨不得把整个百度掀翻。如果是英文歌曲,完全不用这么麻烦,因为有个网站已经帮你整理好了!它就是【tunefind】。


以最近非常火的《权利的游戏》(Game of Thrones)为例,看一下能搜到什么样的结果?可以发现,它会按照不同的Season进行分类:


我们点进最新的《Season 8 》看一下,发现它又细分为了不同的集数,而且可以试听。一旦知道歌名,歌曲下载的事情不就很轻松了吗?



11:书格 —— 可能是最好用的古籍资料下载网站

网址:https://www.shuge.org/

【书格】是一个自由开放的在线古籍图书馆,致力于开放式分享、介绍、推荐有价值的古籍善本。分享内容限定为公共版权领域的书籍(参照标准伯尔尼公约)。


该网站最大限度地还原书籍品貌、内容,借此计划让大家自由、免费地欣赏到那些难以现世的书籍,让大家能从中感受到人类文明进程。

使用方法:

  • 搜索关键词,找到书籍后可直接下载使用,无需注册。




12:Firefox Send —— 火狐出品的临时网盘

网址:https://send.firefox.com/

这是火狐(Firefox)旗下的一个临时网盘,Send 允许上传和加密很大的文件(登陆后最多 可上传2.5GB)来分享到网上。如果你想给同事或朋友分享什么资料,但是又希望分享完后不留在自己电脑或网盘上,就可以试试这个网站。


以 Send 创建的每个链接将在限定下载次数(最多100次)或限定时间内(最多7天) 后过期,有种「阅后即焚」的味道。

轮事件:滚轮

滚动(卷动)事件:滚轮、拖拽滚动条、键盘方向键

<script type="text/javascript">
//滚轮事件:滚轮
//卷动事件:滚轮、拖拽滚动条、键盘?键

//IE和Chrome
gunlun.onmousewheel=function(){
this.innerHTML +="IE和Chrome<br/>";
}
//Firefox
gunlun.addEventListener("DOMMouseScroll", function(){
this.innerHTML +="Firefox<br/>";
})
</script>

判断滚轮方向

<script type="text/javascript">
//滚轮事件:滚轮
//卷动事件:滚轮、拖拽滚动条、键盘?键

//IE和Chrome
gunlun.onmousewheel=function(e){
var ev=e || window.event;
console.log(ev.wheelDelta);//判断滚轮方向的
//上120
//下-120

this.innerHTML +="IE和Chrome<br/>";
}
//Firefox
gunlun.addEventListener("DOMMouseScroll",function(e){
var ev=e || window.event;
console.log(ev.detail);//滚轮方向
//上-3
//下3
this.innerHTML +="Firefox<br/>";
})
</script>

兼容性封装