简体   繁体   中英

Get pixel color in canvas at position in console

I have want to debug my html5 canvas in Chrome console. I want to get the pixel color at position (445, 650).

I tried with this code:

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

When I run those lines one by one, I get:

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

context
null

How can I get a non-null context?

Canvas elements can have only one Rendering Context attached to them. Every time you call getContext(type) after the first initialization, it will return either the same context object if you used the same type parameter, either null if you used an other context's type .

From your markup, it sounds that your canvas has an webgl context attached to it.
So when you call getContext('2d') , it will return null .

Here is an example showing you how to use it from an context you didn't directly initialized. The webgl's getImageData equivalent method is 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);
  });
});

 // 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); }); }); 
 <!-- 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> 

Note that the pixel reading operation becomes async, and will always be one frame late in this implementation, because the webgl context was not initialized with the preserveDrawingBuffer option. If it were, you could make an synchronous method.

There is also one type agnostic way to do it : use an offscreen 2d context and draw the canvas directly on this 2d context.

// 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);
  });
});

 // 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); }); }); 
 <!-- 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> 

Actually your Code works fine.

Here is a HTML/Script-Code which contains the given Information of your question.

<html>
<canvas class="topleft" id="glcanvas" width="479" height="616" tabindex="1" contenteditable="true" style="cursor: default; width: 479px; height: 616px;"></canvas>
<script>
var example = document.getElementById('glcanvas');
var context = example.getContext('2d');
var x=445;
var y=650;
var data = context.getImageData(x, y, 1, 1).data;
alert('R:' + data[0] + ' G:' + data[1] + ' B:' + data[2]);
</script>
</html>

Probably you are running your Script before the initialization of the glcanvas-Object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM