Reputation: 174
Hi i am using a javascript library for my website. Library use webgl to generate animation. So it creates a canvas inside element and shows animation. But i want that animation to look like a donut. I already set border-radius 50% on canvas element and it generates a circle, but i need cut another transparent circle inside canvas. Here is an image of what i want to achive.
I already tried manipulate html elements but so far no success. Also i cant use overlaping element due to background image behind canvas.
Next what i tried was override library function which, but i never done anything in webgl so it went poorly. I add another method that is aplied after animation update. And i achived cut a square inside canvas, but no success with circle. Is there easy way, how to cut a circle in webgl, that i can fit inside my drawEmptyCircle method without any complicated edits in library?
drawEmptyCircle: function(){
gl.enable(gl.SCISSOR_TEST);
gl.scissor(150, 150, 200, 200);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.disable(gl.SCISSOR_TEST);
},
here is fiddle
Upvotes: 0
Views: 554
Reputation: 2703
I think you can't make holes in the Canvas element.
Also i cant use overlaping element due to background image behind canvas.
If you can control the absolute positions of all the elements, this will not be a problem.
As an example I've changed your initial jsfiddle.
First the HTML markup. The outer
class is used to control absolute positions of inner elements. The ontop
class creates a circle above the canvas to imitate the transparency.
<div class="outer bg">
<div class="circle"></div>
<div class="ontop bg"></div>
</div>
In this CSS all the numbers are relative to the original canvas size, that you used in your example (500px x 500px). I used some margin for the canvas (20px) just to show that some more advanced position possible.
.bg {
background-image: url(-your-image-here-);
}
.ontop {
background-position: -145px -145px;
position: absolute;
left: 145px;
top: 145px;
width: 250px;
height: 250px;
background-color: white;
border-radius:50%;
}
.outer {
position:relative;
background-position: 0 0;
width: 540px;
height: 540px;
padding: 20px;
}
Here is the working jsfiddle: click.
To make all other elements (text) visible, I have to use a trick with doubling its HTML elements. Here is the example with the text, which is on a background level (so below the canvas): click.
Upvotes: 1
Reputation:
You say you've never done any WebGL. That suggests you need some tutorials because how to use WebGL not something that can be explained in a single answer on stack overflow.
The most common way to draw a circle with a hole in it in WebGL is to generate triangles that make a circle with a hole in it. This is what all the major GPU accelerated 2D graphics libraries do.
function makeCircleTriangles(innerRadius, outerRadius, divisions, start = 0, end = Math.PI * 2) {
const positions = [];
for (let d = 0; d <= divisions; ++d) {
const u = d / divisions;
const angle = start + (end - start) * u;
const s = Math.sin(angle);
const c = Math.cos(angle);
positions.push(c * innerRadius, s * innerRadius);
positions.push(c * outerRadius, s * outerRadius);
}
const indices = [];
for (let d = 0; d < divisions; ++d) {
const offset = d * 2;
indices.push(offset + 0, offset + 1, offset + 2);
indices.push(offset + 2, offset + 1, offset + 3);
}
return {position, indices};
}
const gl = document.querySelector('canvas').getContext('webgl');
const vs = `
attribute vec4 position;
uniform mat4 matrix;
void main() {
gl_Position = matrix * position;
}
`;
const fs = `
precision highp float;
void main() {
gl_FragColor = vec4(0, .6, 0, 1);
}
`;
// compile shaders, link progrma, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const {positions, indices} = makeCircleTriangles(30, 70, 64);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each array
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: { numComponents: 2, data: positions },
indices,
});
gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// calls gl.uniform
twgl.setUniforms(programInfo, {
matrix: [
2 / gl.canvas.clientWidth, 0, 0, 0,
0, 2 / gl.canvas.clientHeight, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
],
});
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
function makeCircleTriangles(innerRadius, outerRadius, divisions, start = 0, end = Math.PI * 2) {
const positions = [];
for (let d = 0; d <= divisions; ++d) {
const u = d / divisions;
const angle = start + (end - start) * u;
const s = Math.sin(angle);
const c = Math.cos(angle);
positions.push(c * innerRadius, s * innerRadius);
positions.push(c * outerRadius, s * outerRadius);
}
const indices = [];
for (let d = 0; d < divisions; ++d) {
const offset = d * 2;
indices.push(offset + 0, offset + 1, offset + 2);
indices.push(offset + 2, offset + 1, offset + 3);
}
return {positions, indices};
}
<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
The second most common way is to draw a quad (2 triangles) with a texture of a circle with a hole it in.
Upvotes: 1