Is there a way to create a 3D cylinder of the canv

2019-05-07 05:55发布

I'd like to present the canvas as a cylindrical cone, which you can spin like a wheel in both directions. Is this at all possible with JS/CSS3?

2条回答
家丑人穷心不美
2楼-- · 2019-05-07 06:28

The HTML-Canvas is in the actual state only 2D, but the ThreeJS framework seems to be a pretty good solution for 3d-rendering within the canvas-element.

查看更多
姐就是有狂的资本
3楼-- · 2019-05-07 06:35

You should take a look at this new CSS3 feature: the Custom filters / CSS shaders.

Here are some really nice presentations made which describe the whole thing better than I could do (how to enable it on Chrome, how to start, how it works, etc):

Basically, if you're already familiar with shaders and CSS3 transforms, it's all done...

Advantages

  • WebGl-like GPU/3D acceleration
  • JS-free (only GL shaders and CSS)
  • Possibility to combine it with CSS3-transitions

Inconvenients

  • New feature / Only supported by recent versions of some browsers (sometimes protected by a flag)
  • Independant files for the shaders (unsure about that - maybe it's somehow possible to inline them in the html page like with WebGL)
  • While implementing the example described below, I ran into a strange behavior: if the size of the canvas (not the CSS size but the "JS" one, defining the pixels density) gets too big, the shaders doesn't seem to be applied anymore, regardless of the operations you are doing or not on your canvas. I'm quite curious why, so I will try to investigate that.

Example

To answer your more-precise requirements (canvas as a cylindrical cone), I made this small example: http://aldream.net/various/css-shader-demo/cylindricalConeTransformDemo.html. Hover above the canvas to make it wrap into a cone.

It doesn't spin, I just applied a simple transition effect I took from one example in the articles given above, but you should get the idea.

The vertex shader and fragment shader used can be found here:

Simplified & commented Code

  • CSS
    canvas {
        /* ... Prettify it as you wish */

        width: 640px;
        height: 560px;

        -webkit-filter: custom(url(cylindricalConeTransform.vs) /* Vertex-shader */
            mix(url(cylindricalConeTransform.fs) normal source-atop /* Fragment-shader and color-mixing properties */),
            36 2 /* Numbers of vertices */,
            /* Passing the values to the shaders uniforms: */
            amount 0,
            cylinderRadius 0.35,
            cylinderLength 250,
            transform rotateY(0deg) rotateX(0deg));

        -webkit-transition: -webkit-filter linear 1s; /* Transition on the filter for animation. */

    }

    canvas:hover {
        /* Same as above, but with different values for some uniforms. With the CSS-transition, those values will be tweened. */
        filter: custom(url(cylindricalConeTransform.vs) mix(url(cylindricalConeTransform.fs) normal source-atop), 36 2,
            amount 1,
            cylinderRadius 0.35,
            cylinderLength 250,
            transform rotateY(60deg) rotateX(60deg));
    }
  • Vertex-Shader
precision mediump float;

// Built-in attributes
attribute vec4 a_position;
attribute vec2 a_texCoord;

// Built-in uniforms
uniform mat4 u_projectionMatrix;

// Uniforms passed in from CSS
uniform float amount;
uniform float cylinderRadius;
uniform float cylinderLength;
uniform mat4 transform;

// Constants
const float PI = 3.1415;

// Cone function
vec3 computeCylindricalConePosition( vec2 uv, float r, float l ) {
    vec3 p;
    float fi = uv.x * PI * 2.0;

    p.x = r * cos( fi ) * uv.y;
    p.y = r * sin( fi ) * uv.y;
    p.z = (uv.y - 0.5) * l;
    return p;
}

// Main
void main() {
    vec4 position = a_position;

    // Map plane to cone using UV coordinates
    vec3 cone = computeCylindricalConePosition( a_texCoord, cylinderRadius, cylinderLength );

    // Blend plane and cone
    position.xyz = mix( position.xyz, cone, amount );

    // Set vertex position
    gl_Position = u_projectionMatrix * transform * position;
}
  • Fragment-Shader
/** spec: css */
precision mediump float;

void main() {
    css_ColorMatrix = mat4(
        1.0, 0.0, 0.0, 0.0,
        0.0, 1.0, 0.0, 0.0,
        0.0, 0.0, 1.0, 0.0,
        0.0, 0.0, 0.0, 1.0
    );
}
  • HTML
<!doctype html>
<html>
<head>
    ... your meta, css, ...
<body>

    <canvas></canvas>

    <script>
        // Draw what you want in your canvas.
    </script>
</body>
</html>

EDIT : I'm actually not sure if you're asking for a cone or a cylinder, but the difference is small here. If you want the 2nd, you modify the computeCylindricalConePosition() function in the vertex-shader, evaluating p.x and p.y like this instead:

p.x = r * cos( fi ) /* * uv.y */;
p.y = r * sin( fi ) /* * uv.y */;

I hope it helps. I will try to develop my answer once I clarify some points.

查看更多
登录 后发表回答