I'm using code that allows a user to select a color from an image when the mouse hovers that pixel. When using object-fit:contain for the image the colors do not map correctly. The script is acting as if the image is stretched to 100% width / 100% height and not taking into account the object-fit:contain style.
<html>
<head>
<style>
*,*:after,*:before{
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}
body{
width:100%;
height:100%;
position:relative;
}
.thumbnail{
position:relative;
display: block;
overflow:hidden;
height:100%;
width:100%;
}
.thumbnail img {
display: block;
cursor: crosshair;
height: 100%;
width: 100%;
object-fit: contain;
}
#follow {
position: absolute;
width: 55px;
height: 55px;
border-radius:80%;
z-index: 99 !important;
border:2px solid black;
}
#cs{
display:none;
}
#color{
display:none;
}
}
</style>
</head>
<body>
<form id=color>
<input id="hex" type="text" value="hex">
</form>
<div class="preview" id="follow"></div>
<div class="thumbnail">
<img src='[BASE64 IMAGE GOES HERE]'/>
</div>
<canvas id="cs"></canvas>
<script>
// preview follow mouse
$(document).mousemove(function(e) {
$("#follow").css({
left: e.offsetX,
top: e.offsetY
});
});
// vars
var img = _('.thumbnail img'),
canvas = _('#cs'),
preview = _('.preview'),x = '',y = '';
// click function
img.addEventListener('click', function(e){
// chrome
if(e.offsetX) {
x = e.offsetX;
y = e.offsetY;
}
// firefox
else if(e.layerX) {
x = e.layerX;
y = e.layerY;
}
useCanvas(canvas,img,function(){
// get image data
var p = canvas.getContext('2d')
.getImageData(x, y, 1, 1).data;
// show info
color.innerHTML = '<textarea id="hex" type="text" >'+rgbToHex(p[0],p[1],p[2])+'</textarea>';
});
},false);
// preview function mousemove
img.addEventListener('mousemove', function(e){
// chrome
if(e.offsetX) {
x = e.offsetX;
y = e.offsetY;
}
// firefox
else if(e.layerX) {
x = e.layerX;
y = e.layerY;
}
useCanvas(canvas,img,function(){
// get image data
var p = canvas.getContext('2d')
.getImageData(x, y, 1, 1).data;
// show preview color
preview.style.background = rgbToHex(p[0],p[1],p[2]);
});
},false);
// canvas function
function useCanvas(el,image,callback){
el.width = image.width; // img width
el.height = image.height; // img height
// draw image in canvas tag
el.getContext('2d')
.drawImage(image, 0, 0, image.width, image.height);
return callback();
}
// short querySelector
function _(el){
return document.querySelector(el);
};
// convert rgba to hex
// http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
function findPos(obj) {
var curleft = 0, curtop = 0;
if (obj.offsetParent) {
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
} while (obj = obj.offsetParent);
return { x: curleft, y: curtop };
}
return undefined;
}
</script>
</body>
</html>
Yes, that is because the code is using an unscaled
canvas
copy of the image.The surest way to fix this is to always display the image at 100% scale instead of using
fit
. Allow panning around the image if needed so that the user can still click any part of it. (You may need to subtract the offset dimensions of the pan from the xy coordinates of the mouse event.)You could also detect the resized scale and then use this to multiply the offsets when used in
canvas
, but this is going to lead to inaccuracies due to rounding and so on.You may also want to avoid browsers smoothing the displayed image to avoid displaying pixels that don’t actually exist in the source image:
image-rendering: pixelated
https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering