1 <script type="text/javascript" charset="utf-8"> 2 /** 3 * A reusable HTML Import to enable zooming on images. 4 * 5 * To use, simply include this HTML Import and add the class 'zoom' to any 6 * images you want zoomable. 7 * 8 * <link rel='import' type='text/html' href='/res/imp/zoom.html'> 9 * 10 * <img src="http://..." class="zoom"/> 11 * 12 * Any number of images on a page can be zoomable. 13 * 14 * If you want to display the rgb colors of the pixel at the center of the 15 * zoom then add an id of 'zoomHex' to any element that supports 16 * textContent, such as a div, p, span, etc. 17 * 18 * <p id=zoomHex></p> 19 * 20 * Note that HTML Imports need to be polyfilled in the near term. 21 */ 22 (function () { 23 function onLoad() { 24 var PIXELS = 20; // The number of pixels in width and height in a zoom. 25 var clientX = 0; 26 var clientY = 0; 27 var lastClientX = 0; 28 var lastClientY = 0; 29 var ctx = null; // The 2D canvas context of the zoom. 30 var currentImage = null; // The img node we are zooming for, otherwise null. 31 var hex = document.getElementById('zoomHex'); 32 var canvasCopy = null; 33 function zoomMove(e) { 34 clientX = e.clientX; 35 clientY = e.clientY; 36 } 37 function zoomMouseDown(e) { 38 e.preventDefault(); 39 // Only do zooming on the primary mouse button. 40 if (e.button != 0) { 41 return 42 } 43 currentImage = e.target; 44 clientX = e.clientX; 45 clientY = e.clientY; 46 lastClientX = clientX-1; 47 lastClientY = clientY-1; 48 document.body.style.cursor = 'crosshair'; 49 canvas = document.createElement('canvas'); 50 canvas.width = 1024; 51 canvas.height = 1024; 52 canvas.classList.add('zoomCanvas'); 53 ctx = canvas.getContext('2d'); 54 ctx.imageSmoothingEnabled = false; 55 this.parentNode.insertBefore(canvas, this); 56 // Copy the image over to a canvas so we can read RGBA values for each point. 57 if (hex) { 58 canvasCopy = document.createElement('canvas'); 59 canvasCopy.width = currentImage.width; 60 canvasCopy.height = currentImage.height; 61 canvasCopy.id = 'zoomCopy'; 62 canvasCopy.getContext('2d').drawImage(currentImage, 0, 0, currentImage.width, currentImage.height); 63 this.parentNode.insertBefore(canvasCopy, this); 64 } 65 document.body.addEventListener('pointermove', zoomMove, true); 66 document.body.addEventListener('pointerup', zoomFinished); 67 document.body.addEventListener('pointerleave', zoomFinished); 68 // Kick off the drawing. 69 setTimeout(drawZoom, 1); 70 } 71 function hexify(i) { 72 var s = i.toString(16).toUpperCase(); 73 // Pad out to two hex digits if necessary. 74 if (s.length < 2) { 75 s = '0' + s; 76 } 77 return s; 78 } 79 function drawZoom() { 80 if (currentImage) { 81 // Only draw if the mouse has moved from the last time we drew. 82 if (lastClientX != clientX || lastClientY != clientY) { 83 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 84 var x = clientX - currentImage.x; 85 var y = clientY - currentImage.y; 86 var dx = Math.floor(ctx.canvas.width/PIXELS); 87 var dy = Math.floor(ctx.canvas.height/PIXELS); 88 ctx.lineWidth = 1; 89 ctx.strokeStyle = '#000'; 90 // Draw out each pixel as a rect on the target canvas, as this works around 91 // FireFox doing a blur as it copies from one canvas to another. 92 var colors = canvasCopy.getContext('2d').getImageData(x, y, PIXELS, PIXELS).data; 93 for (var i=0; i<PIXELS; i++) { 94 for (var j=0; j<PIXELS; j++) { 95 var offset = (j*PIXELS+i)*4; // Offset into the colors array. 96 ctx.fillStyle = 'rgba(' + colors[offset] + ', ' + colors[offset+1] + ', ' + colors[offset+2] + ', ' + colors[offset+3]/255.0 + ')'; 97 ctx.fillRect(i*dx, j*dy, dx-1, dy-1); 98 // Box and label one selected pixel with its rgba values. 99 if (hex && i==PIXELS/2 && j == PIXELS/2) { 100 ctx.strokeRect(i*dx, j*dy, dx-1, dy-1); 101 hex.textContent = 'rgba(' 102 + colors[offset] + ', ' 103 + colors[offset+1] + ', ' 104 + colors[offset+2] + ', ' 105 + colors[offset+3] + ') ' 106 + hexify(colors[offset]) 107 + hexify(colors[offset+1]) 108 + hexify(colors[offset+2]) 109 + hexify(colors[offset+3]); 110 } 111 } 112 } 113 lastClientX = clientX; 114 lastClientY = clientY; 115 } 116 setTimeout(drawZoom, 1000/30); 117 } 118 } 119 function zoomFinished() { 120 currentImage = null; 121 if (hex) { 122 hex.textContent = ''; 123 } 124 document.body.style.cursor = 'default'; 125 ctx.canvas.parentNode.removeChild(ctx.canvas); 126 canvasCopy.parentNode.removeChild(canvasCopy); 127 document.body.removeEventListener('pointermove', zoomMove, true); 128 document.body.removeEventListener('pointerup', zoomFinished); 129 document.body.removeEventListener('pointerleave', zoomFinished); 130 } 131 132 var zoomables = document.body.querySelectorAll('.zoom'); 133 for (var i=0; i<zoomables.length; i++) { 134 zoomables[i].addEventListener('pointerdown', zoomMouseDown); 135 } 136 } 137 138 // If loaded via HTML Imports then DOMContentLoaded will be long done. 139 if (document.readyState != "loading") { 140 onLoad(); 141 } else { 142 this.addEventListener('DOMContentLoaded', onLoad); 143 } 144 })(); 145 </script> 146 147 <style type="text/css" media="screen"> 148 .zoom { 149 cursor: crosshair; 150 } 151 152 .zoomCanvas { 153 position: absolute; 154 width: vmin; 155 height: vmin; 156 top: 3em; 157 right: 1em; 158 z-index: -1; 159 } 160 161 #zoomCopy { 162 display: none; 163 } 164 165 #zoomHex { 166 text-shadow: 1px 1px #eee; 167 } 168 </style> 169 170