异步绘制线段
1.先画一个点
2.一秒钟后,在左下角画一个点
3.两秒钟后,我再画一条线段
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="webgl" width="200" height="200"></canvas>
<script>
const webgl = document.getElementById('webgl')
const gl = webgl.getContext('webgl')
// 创建着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
// 绑定数据源
// 声明顶点着色器 attribute 变量
gl.shaderSource(vertexShader, `
attribute vec4 a_Position;
void main(){
gl_Position = a_Position;
gl_PointSize = 20.0;
}
`)
gl.shaderSource(fragmentShader, `
void main(){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`)
// 编译着色器
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
// 创建着色器程序
const program = gl.createProgram()
// 绑定着色器
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
// 连接着色器
gl.linkProgram(program)
// 使用着色器
gl.useProgram(program)
// 顶点数据
let points = [0, 0.2]
// 创建缓冲区
const vertexBuffer = gl.createBuffer()
// 绑定缓冲区
// target 要把缓冲区放在webgl系统中的什么位置, buffer 缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
// 写入数据
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW)
// 获取到顶点着色器中变量
const a_Position = gl.getAttribLocation(program, 'a_Position')
// 从当前绑定的缓冲区中读取顶点数据(index, size, type, normalized是否顶点数据归一, stride相邻两个顶点间的字节数, offset从缓冲区的什么位置开始存储变量)
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)
// 开启顶点数据的批处理功能
gl.enableVertexAttribArray(a_Position)
gl.drawArrays(gl.POINTS, 0, 1);
// 一秒钟后,向顶点数据中再添加的一个顶点,修改缓冲区数据,然后清理画布,绘制顶点
setTimeout(() => {
points.push(-0.2, -0.1)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW)
gl.drawArrays(gl.POINTS, 0, 2);
}, 1000)
// 两秒钟后,清理画布,绘制顶点,绘制线条
setTimeout(() => {
gl.drawArrays(gl.POINTS, 0, 2);
gl.drawArrays(gl.LINE_STRIP, 0, 2);
}, 2000)
</script>
</body>
</html>
封装
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="webgl" width="200" height="200"></canvas>
<script>
const webgl = document.getElementById('webgl')
const gl = webgl.getContext('webgl')
// 创建着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
// 绑定数据源
// 声明顶点着色器 attribute 变量
gl.shaderSource(vertexShader, `
attribute vec4 a_Position;
void main(){
gl_Position = a_Position;
gl_PointSize = 20.0;
}
`)
gl.shaderSource(fragmentShader, `
void main(){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`)
// 编译着色器
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
// 创建着色器程序
const program = gl.createProgram()
// 绑定着色器
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
// 连接着色器
gl.linkProgram(program)
// 使用着色器
gl.useProgram(program)
const defAttr = () => ({
gl: null,
vertices: [],
geoData: [],
size: 2,
attrName: 'a_Position',
count: 0,
types: ['POINTS'],
})
class Poly {
constructor(attr) {
Object.assign(this, defAttr(), attr)
this.init()
}
init() {
const { attrName, size, gl } = this
if (!gl) { return }
const vertexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
this.updateBuffer()
const a_Position = gl.getAttribLocation(program, attrName)
gl.vertexAttribPointer(a_Position, size, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(a_Position)
}
addVertice(...params) {
this.vertices.push(...params)
this.updateBuffer()
}
popVertice() {
const { vertices, size } = this
const len = vertices.length
vertices.splice(len - size, len)
this.updateCount()
}
setVertice(ind, ...params) {
const { vertices, size } = this
const i = ind * size
params.forEach((param, paramInd) => {
vertices[i + paramInd] = param
})
}
updateBuffer() {
const { gl, vertices } = this
this.updateCount()
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
}
updateCount() {
this.count = this.vertices.length / this.size
}
updateVertices(params) {
const { geoData } = this
const vertices = []
geoData.forEach(data => {
params.forEach(key => {
vertices.push(data[key])
})
})
this.vertices = vertices
}
draw(types = this.types) {
const { gl, count } = this
for (let type of types) {
gl.drawArrays(gl[type], 0, count);
}
}
}
const poly = new Poly({
gl,
vertices: [0, 0.2]
})
poly.draw(['POINTS'])
setTimeout(() => {
poly.addVertice(-0.2, -0.1)
poly.draw(['POINTS'])
}, 1000)
setTimeout(() => {
poly.draw(['POINTS', 'LINE_STRIP'])
}, 2000)
</script>
</body>
</html>
根据鼠标点击绘制点和线
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="webgl"></canvas>
<script>
const webgl = document.getElementById('webgl')
webgl.width = window.innerWidth
webgl.height = window.innerHeight
const gl = webgl.getContext('webgl')
// 创建着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
// 绑定数据源
// 声明顶点着色器 attribute 变量
gl.shaderSource(vertexShader, `
attribute vec4 a_Position;
void main(){
gl_Position = a_Position;
gl_PointSize = 20.0;
}
`)
gl.shaderSource(fragmentShader, `
void main(){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`)
// 编译着色器
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
// 创建着色器程序
const program = gl.createProgram()
// 绑定着色器
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
// 连接着色器
gl.linkProgram(program)
// 使用着色器
gl.useProgram(program)
const defAttr = () => ({
gl: null,
vertices: [],
geoData: [],
size: 2,
attrName: 'a_Position',
count: 0,
types: ['POINTS'],
})
class Poly {
constructor(attr) {
Object.assign(this, defAttr(), attr)
this.init()
}
init() {
const { attrName, size, gl } = this
if (!gl) { return }
const vertexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
this.updateBuffer()
const a_Position = gl.getAttribLocation(program, attrName)
gl.vertexAttribPointer(a_Position, size, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(a_Position)
}
addVertice(...params) {
this.vertices.push(...params)
this.updateBuffer()
}
popVertice() {
const { vertices, size } = this
const len = vertices.length
vertices.splice(len - size, len)
this.updateCount()
}
setVertice(ind, ...params) {
const { vertices, size } = this
const i = ind * size
params.forEach((param, paramInd) => {
vertices[i + paramInd] = param
})
}
updateBuffer() {
const { gl, vertices } = this
this.updateCount()
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
}
updateCount() {
this.count = this.vertices.length / this.size
}
updateVertices(params) {
const { geoData } = this
const vertices = []
geoData.forEach(data => {
params.forEach(key => {
vertices.push(data[key])
})
})
this.vertices = vertices
}
draw(types = this.types) {
const { gl, count } = this
for (let type of types) {
gl.drawArrays(gl[type], 0, count);
}
}
}
const poly = new Poly({
gl,
types: ['POINTS', 'LINE_STRIP']
})
webgl.addEventListener('click', (e) => {
// 获取鼠标距离视口尺寸的距离
const { clientX, clientY } = e
// 获取cavans坐标宽高,距离视口尺寸的距离
const { left, top, width, height } = webgl.getBoundingClientRect()
// 鼠标在canvas中的位置
const [cssX, cssY] = [clientX - left, clientY - top]
// canvas坐标转webgl坐标
// canvas画布的中心位置
const [halfWidth, halfHeight] = [width / 2, height / 2]
// 鼠标基于webgl坐标的中心位置
const [xBaseCenter, yBaseCenter] = [cssX - halfWidth, cssY - halfHeight]
// 解决y方向的差异
const yBaseCenterTop = -yBaseCenter
// 解决坐标基底的差异
const [x, y] = [xBaseCenter / halfWidth, yBaseCenterTop / halfHeight]
poly.addVertice(x, y)
poly.draw()
})
</script>
</body>
</html>
根据鼠标绘制多条点和线
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="webgl"></canvas>
<script>
const webgl = document.getElementById('webgl')
webgl.width = window.innerWidth
webgl.height = window.innerHeight
const gl = webgl.getContext('webgl')
// 创建着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
// 绑定数据源
// 声明顶点着色器 attribute 变量
gl.shaderSource(vertexShader, `
attribute vec4 a_Position;
void main(){
gl_Position = a_Position;
gl_PointSize = 20.0;
}
`)
gl.shaderSource(fragmentShader, `
void main(){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`)
// 编译着色器
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
// 创建着色器程序
const program = gl.createProgram()
// 绑定着色器
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
// 连接着色器
gl.linkProgram(program)
// 使用着色器
gl.useProgram(program)
const defAttr = () => ({
gl: null,
vertices: [],
geoData: [],
size: 2,
attrName: 'a_Position',
count: 0,
types: ['POINTS'],
})
class Poly {
constructor(attr) {
Object.assign(this, defAttr(), attr)
this.init()
}
init() {
const { attrName, size, gl } = this
if (!gl) { return }
const vertexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
this.updateBuffer()
const a_Position = gl.getAttribLocation(program, attrName)
gl.vertexAttribPointer(a_Position, size, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(a_Position)
}
addVertice(...params) {
this.vertices.push(...params)
this.updateBuffer()
}
popVertice() {
const { vertices, size } = this
const len = vertices.length
vertices.splice(len - size, len)
this.updateCount()
}
setVertice(ind, ...params) {
const { vertices, size } = this
const i = ind * size
params.forEach((param, paramInd) => {
vertices[i + paramInd] = param
})
}
updateBuffer() {
const { gl, vertices } = this
this.updateCount()
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
}
updateCount() {
this.count = this.vertices.length / this.size
}
updateVertices(params) {
const { geoData } = this
const vertices = []
geoData.forEach(data => {
params.forEach(key => {
vertices.push(data[key])
})
})
this.vertices = vertices
}
draw(types = this.types) {
const { gl, count } = this
for (let type of types) {
gl.drawArrays(gl[type], 0, count);
}
}
}
// 容器来承载(多线)绘制
class Sky {
constructor(gl) {
this.gl = gl
this.children = []
}
add(obj) {
obj.gl = this.gl
this.children.push(obj)
}
updateVertices(params) {
this.children.forEach(ele => {
ele.updateVertices(params)
})
}
draw() {
this.children.forEach(ele => {
ele.init()
ele.draw()
})
}
}
const sky = new Sky(gl)
// 当前正在绘制的多边形
let poly = null
//删除最后一个顶点
function popVertice() {
poly.popVertice()
poly = null
}
// 取消右击提示
webgl.oncontextmenu = function () {
return false
}
// 鼠标点击事件
webgl.addEventListener("mousedown", (event) => {
if (event.button === 2) {
// 右击删除正在绘制的点
popVertice()
} else {
// 获取鼠标距离视口尺寸的距离
const { clientX, clientY } = event
// 获取cavans坐标宽高,距离视口尺寸的距离
const { left, top, width, height } = webgl.getBoundingClientRect()
// 鼠标在canvas中的位置
const [cssX, cssY] = [clientX - left, clientY - top]
// canvas坐标转webgl坐标
// canvas画布的中心位置
const [halfWidth, halfHeight] = [width / 2, height / 2]
// 鼠标基于webgl坐标的中心位置
const [xBaseCenter, yBaseCenter] = [cssX - halfWidth, cssY - halfHeight]
// 解决y方向的差异
const yBaseCenterTop = -yBaseCenter
// 解决坐标基底的差异
const [x, y] = [xBaseCenter / halfWidth, yBaseCenterTop / halfHeight]
if (poly) {
poly.addVertice(x, y)
} else {
crtPoly(x, y)
}
}
render()
});
//鼠标移动
webgl.addEventListener("mousemove", (event) => {
if (poly) {
// 获取鼠标距离视口尺寸的距离
const { clientX, clientY } = event
// 获取cavans坐标宽高,距离视口尺寸的距离
const { left, top, width, height } = webgl.getBoundingClientRect()
// 鼠标在canvas中的位置
const [cssX, cssY] = [clientX - left, clientY - top]
// canvas坐标转webgl坐标
// canvas画布的中心位置
const [halfWidth, halfHeight] = [width / 2, height / 2]
// 鼠标基于webgl坐标的中心位置
const [xBaseCenter, yBaseCenter] = [cssX - halfWidth, cssY - halfHeight]
// 解决y方向的差异
const yBaseCenterTop = -yBaseCenter
// 解决坐标基底的差异
const [x, y] = [xBaseCenter / halfWidth, yBaseCenterTop / halfHeight]
poly.setVertice(poly.count - 1, x, y)
render()
}
});
//创建多边形
function crtPoly(x, y) {
poly = new Poly({
vertices: [x, y, x, y],
types: ['POINTS', 'LINE_STRIP']
})
sky.add(poly)
}
// 渲染方法
function render() {
sky.draw()
}
</script>
</body>
</html>