获取画布中控制台位置处的像素颜色

TSR*_*TSR 3 html javascript debugging canvas

我想在 Chrome 控制台中调试我的 html5 画布。我想获取位置 (445, 650) 处的像素颜色。

我尝试使用以下代码:

var example = document.getElementById('glcanvas');
var context = example.getContext('2d');
var data = context.getImageData(x, y, 1, 1).data;
Run Code Online (Sandbox Code Playgroud)

当我一行一行地运行这些行时,我得到:

example
<canvas class="topleft" id="glcanvas" width="479" height="616" tabindex="1" contenteditable="true" style="cursor: default; width: 479px; height: 616px;"></canvas>

context
null
Run Code Online (Sandbox Code Playgroud)

如何获得非空上下文?

Kai*_*ido 8

画布元素只能附加一个渲染上下文。每次在getContext(type)第一次初始化后调用时,如果您使用相同的参数,它将返回相同的上下文对象type,或者null如果您使用其他上下文的type.

从您的标记来看,您的画布似乎附加了一个 webgl 上下文。
所以当你调用时getContext('2d'),它就会返回null

这是一个示例,向您展示如何在未直接初始化的上下文中使用它。webgl 的getImageData 等效方法readPixels().

// we have access to the DOM element
var canvas = document.querySelector('canvas');
// we need to get the correct context type, or it will return null
var gl = canvas.getContext('webgl') || canvas.getContext('webgl2');
// where we'll store our pixels info
var pixels = new Uint8Array(4);

canvas.addEventListener('click', function(e) {
  var x = e.clientX - canvas.offsetLeft;
  var y = e.clientY - canvas.offsetTop;
  // we need to call it in the same execution flow as 'render' because webgl erase the drawing buffer by default
  // this can be done by stacking our code in the next frame.
  requestAnimationFrame(function() {
    gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    console.log(pixels);
  });
});
Run Code Online (Sandbox Code Playgroud)

// we have access to the DOM element
var canvas = document.querySelector('canvas');
// we need to get the correct context type, or it will return null
var gl = canvas.getContext('webgl') || canvas.getContext('webgl2');
// where we'll store our pixels info
var pixels = new Uint8Array(4);

canvas.addEventListener('click', function(e) {
  var x = e.clientX - canvas.offsetLeft;
  var y = e.clientY - canvas.offsetTop;
  // we need to call it in the same execution flow as 'render' because webgl erase the drawing buffer by default
  // this can be done by stacking our code in the next frame.
  requestAnimationFrame(function() {
    gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    console.log(pixels);
  });
});
Run Code Online (Sandbox Code Playgroud)
<!-- 
Example of external code, on which we don't have direct access.
Taken from https://github.com/mrdoob/three.js/blob/master/examples/webgl_geometry_cube.html
-->

<base href="https://threejs.org/examples/">
<script src="../build/three.js"></script>
<script>
(function(){
  var camera, scene, renderer;
  var mesh;
  init();
  animate();
  console.clear();

  function init() {
    camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
    camera.position.z = 400;
    scene = new THREE.Scene();
    var geometry = new THREE.BoxBufferGeometry(200, 200, 200);
    var material = new THREE.MeshBasicMaterial();
    mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    //
    window.addEventListener('resize', onWindowResize, false);
  }

  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  }

  function animate() {
    requestAnimationFrame(animate);
    mesh.rotation.x += 0.005;
    mesh.rotation.y += 0.01;
    renderer.render(scene, camera);
  }
})();
</script>
Run Code Online (Sandbox Code Playgroud)

请注意,像素读取操作变为异步,并且在此实现中始终会晚一帧,因为 webgl 上下文未使用该preserveDrawingBuffer选项进行初始化。如果是的话,您可以创建一个同步方法。

还有一种与类型无关的方法:使用屏幕外 2d 上下文并直接在此 2d 上下文上绘制画布。

// the canvas we want to read
var target = document.querySelector('canvas');
// the canvas we'll use to read the target on
var reader = document.createElement('canvas');
var ctx = reader.getContext('2d');

target.addEventListener('click', function(e) {
  var x = e.clientX - target.offsetLeft;
  var y = e.clientY - target.offsetTop;
  // same preserveDrawingBuffer workaround
  requestAnimationFrame(function() {
    // move the target image in the top left corner of our reader,
    //  because we want only a single pixel
    ctx.drawImage(target, -x, -y); 
    var pixels = ctx.getImageData(0, 0, 1, 1);
    console.log(pixels);
  });
});
Run Code Online (Sandbox Code Playgroud)

// the canvas we want to read
var target = document.querySelector('canvas');
// the canvas we'll use to read the target on
var reader = document.createElement('canvas');
var ctx = reader.getContext('2d');

target.addEventListener('click', function(e) {
  var x = e.clientX - target.offsetLeft;
  var y = e.clientY - target.offsetTop;
  // same preserveDrawingBuffer workaround
  requestAnimationFrame(function() {
    // move the target image in the top left corner of our reader,
    //  because we want only a single pixel
    ctx.drawImage(target, -x, -y); 
    var pixels = ctx.getImageData(0, 0, 1, 1);
    console.log(pixels);
  });
});
Run Code Online (Sandbox Code Playgroud)
<!-- 
Example of external code, on which we don't have direct access.
Taken from https://github.com/mrdoob/three.js/blob/master/examples/webgl_geometry_cube.html
-->

<base href="https://threejs.org/examples/">
<script src="../build/three.js"></script>
<script>
  (function() {
    var camera, scene, renderer;
    var mesh;
    init();
    animate();
    console.clear();

    function init() {
      camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
      camera.position.z = 400;
      scene = new THREE.Scene();
      var geometry = new THREE.BoxBufferGeometry(200, 200, 200);
      var material = new THREE.MeshBasicMaterial();
      mesh = new THREE.Mesh(geometry, material);
      scene.add(mesh);
      renderer = new THREE.WebGLRenderer();
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);
      //
      window.addEventListener('resize', onWindowResize, false);
    }

    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }

    function animate() {
      requestAnimationFrame(animate);
      mesh.rotation.x += 0.005;
      mesh.rotation.y += 0.01;
      renderer.render(scene, camera);
    }
  })();
</script>
Run Code Online (Sandbox Code Playgroud)