移动设备上的全屏,全dpi WebGL画布

时间:2016-04-26 08:25:30

标签: android ios mobile webgl webvr

有什么办法可以让移动设备1到1上的画布大小到设备屏幕实际像素大小?

这对于直接来自网站的Google纸板(VR)应用程序支持非常重要。 (在这种情况下,dpi真的很重要)

目前,使用viewport target-densitydpi的唯一方法已弃用,不适用于现代浏览器。

1 个答案:

答案 0 :(得分:2)

最简单的方法是使用CSS来决定画布的大小。然后使用canvas.clientWidthcanvas.clientHeight查看浏览器以CSS像素显示画布的大小。现在将其乘以window.devicePixelRatio以获得CSS像素所代表的设备像素数,并将画布的内容/绘图缓冲区大小设置为匹配。

示例:

  1. 制作画布

    <canvas id="c"></canvas>  
    
  2. 使用CSS

    设置其大小

    这里我将画布设为视口(窗口)的大小。

    body {
      margin: 0;
    }
    canvas {
      width: 100vw;
      height: 100vh; 
      display: block;
    }
    
  3. 查看显示的尺寸并与其绘图缓冲区大小相匹配。

    function resizeToMatchDisplaySize(canvas) {
      var displayWidth  = canvas.clientWidth  * window.devicePixelRatio;
      var displayHeight = canvas.clientHeight * window.devicePixelRatio;
      if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
        canvas.width  = displayWidth;
        canvas.height = displayHeight;
        return true;
      }
      return false;
    }
    
  4. 在渲染每个帧之前调整大小

    function render() {
      // resize before rendering in case of resizing or rotation
      if (resizeToMatchDisplaySize(gl.canvas)) {
        gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); 
      }
    
    
      // render scene
    
      requestAnimationFrame(render);
    }
    requestAnimationFrame(render);
    
  5. 这是一个绘制一些线条的例子

    &#13;
    &#13;
    var gl = document.querySelector("#c").getContext("webgl", { alpha: false });
    var programInfo = twgl.createProgramInfo(gl, ['vs', 'fs']);
    var ids = [];
    for (var i = 0; i < 500; ++i) {
      ids.push(i);
    }
    var arrays = {
      vertexId: { size:1, data: ids},
    };
    var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
    
    var uniforms = {
      time: 0,
      numIds: ids.length,
      resolution: [0, 0],
    };
    
    function resizeToMatchDisplaySize(canvas) {
      var displayWidth  = canvas.clientWidth  * window.devicePixelRatio;
      var displayHeight = canvas.clientHeight * window.devicePixelRatio;
      if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
        canvas.width  = displayWidth;
        canvas.height = displayHeight;
        return true;
      }
      return false;
    }
    
    function render(time) {
      // resize before rendering in case of resizing or rotation
      if (resizeToMatchDisplaySize(gl.canvas)) {
        gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
      }
      
      // render scene
      gl.useProgram(programInfo.program);
      uniforms.time = time * 0.001;
      uniforms.resolution[0] = gl.drawingBufferWidth;
      uniforms.resolution[1] = gl.drawingBufferHeight;
      twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
      twgl.setUniforms(programInfo, uniforms);
      twgl.drawBufferInfo(gl, gl.LINES, bufferInfo);
      
      requestAnimationFrame(render);
    }
    requestAnimationFrame(render);
    &#13;
    body {
      margin: 0;
    }
    canvas {
      width: 100vw;
      height: 100vh;
      display: block;
    }
    &#13;
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    <canvas id="c"></canvas>
      <script id="vs" type="notjs">
    attribute float vertexId;
    
    varying vec4 v_color;
    
    uniform float numIds;
    uniform float time;
    uniform vec2 resolution;
    
    #define PI radians(180.0)
    
    vec3 hsv2rgb(vec3 c) {
      c = vec3(c.x, clamp(c.yz, 0.0, 1.0));
      vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
      vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
      return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
    }
    
    
    void main() {
      vec4 offsets = vec4(
        sin(time), 
        sin(time * .13) * PI * 2.,
        sin(time * .43) * .5 + 1.,
        cos(time * .17) * .5 + .5);
    
      vec4 centers = vec4(
        sin(time * .163) * .5,
        cos(time * .267) * .5,
        sin(time * .367) * .5,
        cos(time * .497) * .5);
    
      vec4 mult = vec4(
        1.,
        (sin(time * .1) * .5 + .5) * 3.,
        0.,
        0.);
    
      vec2 position = vec2(vertexId / numIds, mod(vertexId, 2.));
      vec2 offset = mix(offsets.xz, offsets.yw, position.y);
      float a = mult.x * position.x * PI * 2.0 + offset.x;//mix(u_offsets.x, u_offsets.y, a_position.y);
      float c = cos(a * mult.y);
      vec2 xy = vec2(
        cos(a),
        sin(a)) * c * offset.y +
        mix(centers.xy, centers.zw, position.y);
      vec2 aspect = vec2(resolution.y / resolution.x, 1);
      gl_Position = vec4(xy * aspect, 0, 1);
      
      float hue = position.x;
      float sat = 1.;
      float val = 1.;
      v_color = vec4(hsv2rgb(vec3(hue, sat, val)), 1);
    }
      </script>
      <script id="fs" type="notjs">
    precision mediump float;
    varying vec4 v_color;
    void main() {
      gl_FragColor = v_color;
    }
      </script>
      <script src="https://twgljs.org/dist/twgl-full.min.js"></script>
    &#13;
    &#13;
    &#13;

    请注意,您已经提出了2个问题。如何获得原生1x1像素以及如何全屏显示。全屏更难。 iOS Safari不支持fullscreen API。 Chrome for Android可以,但它需要您的网站从HTTPS提供。

    您还应该将其放在<head>部分

    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    

    根据您的需要将user-scalable设置为yesno