Wrong rectangle size in canvas

I am implementing a color picker. There is a problem with rendering. When I call c.fillRect(0, 0, 100, 80); , the size of this rectangle is 103x42 px instead of 100x80. What is wrong here?

In addition, the rectangles are smoothed. Is it necessary to compensate for the position (0.5, 0.5) to avoid AA? I did not use any coordinate system transformations.

 colorSlider = function($e, color) { this._$canvas = $('<canvas></canvas>'); this._c = this._$canvas[0].getContext('2d'); this._color = color || { r: 0, g: 0, b: 0 }; this._$canvas.width('310px'); this._$canvas.height('80px'); $e.append(this._$canvas); this._render(); var me = this; this._$canvas.mousedown(function(e) { me._mouseDown.call(me, e) }); this._$canvas.mouseup(function(e) { me._mouseUp.call(me, e) }); this._$canvas.mousemove(function(e) { me._mouseMove.call(me, e) }); this._dragChannel = 0; } colorSlider.prototype._pointInRect = function(x, y, rect) { return x >= rect.x && x <= rect.x + rect.w && y >= rect.y && y <= rect.y + rect.h; } colorSlider.prototype._findTarget = function(event) { var x = event.offsetX; var y = event.offsetY; console.log(x, y, this._rectR); if (this._pointInRect(x, y, this._rectRThumb)) { return { target: 1, value: x - this._rectR.x }; } if (this._pointInRect(x, y, this._rectGThumb)) { return { target: 2, value: x - this._rectG.x }; } if (this._pointInRect(x, y, this._rectBThumb)) { return { target: 3, value: x - this._rectB.x }; } if (this._pointInRect(x, y, this._rectR)) { return { target: 4, value: x - this._rectR.x }; } if (this._pointInRect(x, y, this._rectG)) { return { target: 5, value: x - this._rectG.x }; } if (this._pointInRect(x, y, this._rectB)) { return { target: 6, value: x - this._rectB.x }; } return null; } colorSlider.prototype._mouseDown = function(event) { this._dragChannel = 0; var target = this._findTarget(event); if (target) { switch (target.target) { case 1: this._dragChannel = 1; break; case 2: this._dragChannel = 2; break; case 3: this._dragChannel = 3; break; case 4: this._color.r = target.value; break; case 5: this._color.g = target.value; break; case 6: this._color.b = target.value; break; } this._render(); } }; colorSlider.prototype._mouseUp = function(event) { //console.log('mouseUp'); }; colorSlider.prototype._mouseMove = function(event) { //console.log('mouseMove', event); }; colorSlider.prototype.padding = 4; colorSlider.prototype._render = function() { var padding = this.padding; var thickness = 16; var c = this._c; var w = 255; var h = this._$canvas.height(); c.clearRect(0, 0, this._$canvas.width(), this._$canvas.height()); var gradient = c.createLinearGradient(padding, 0, w, 0); c.fillStyle = gradient; gradient.addColorStop(0, this.colorToHex({ r: 0, g: this._color.g, b: this._color.b })); gradient.addColorStop(1, this.colorToHex({ r: 255, g: this._color.g, b: this._color.b })); c.fillRect(padding, padding, w, thickness); c.lineWidth = 0; c.fillRect(0, 0, 100, 80); this._rectR = { x: padding, y: padding, w: w, h: thickness }; gradient = c.createLinearGradient(padding, 0, w, 0); c.fillStyle = gradient; gradient.addColorStop(0, this.colorToHex({ r: this._color.r, g: 0, b: this._color.b })); gradient.addColorStop(1, this.colorToHex({ r: this._color.r, g: 255, b: this._color.b })); c.fillRect(padding, padding + thickness + 2 * padding, w, thickness); this._rectG = { x: padding, y: padding + thickness + 2 * padding, w: w, h: thickness }; gradient = c.createLinearGradient(padding, 0, w, 0); c.fillStyle = gradient; gradient.addColorStop(0, this.colorToHex({ r: this._color.r, g: this._color.g, b: 0 })); gradient.addColorStop(1, this.colorToHex({ r: this._color.r, g: this._color.g, b: 255 })); c.fillRect(padding, padding + 2 * (thickness + 2 * padding), w, thickness); this._rectB = { x: padding, y: padding + 2 * (thickness + 2 * padding), w: w, h: thickness }; c.lineWidth = 2; c.fillStyle = "white"; c.strokeStyle = "#888888"; this._rectRThumb = { x: padding + this._color.r - 2, y: padding / 2, w: 8, h: 20, r: 2 }; this.drawRoundedRectangle(c, this._rectRThumb); this._rectGThumb = { x: padding + this._color.g - 2, y: padding / 2 + 2 * padding + thickness, w: 8, h: 20, r: 2 }; this.drawRoundedRectangle(c, this._rectGThumb); this._rectBThumb = { x: padding + this._color.b - 2, y: padding / 2 + 2 * (2 * padding + thickness), w: 8, h: 20, r: 2 }; this.drawRoundedRectangle(c, this._rectBThumb); }; colorSlider.prototype.colorToHex = function(color) { var c = '#' + (color.r + 256).toString(16).substr(1, 2) + (color.g + 256).toString(16).substr(1, 2) + (color.b + 256).toString(16).substr(1, 2); console.log(c); return c; }; // http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas colorSlider.prototype.drawRoundedRectangle = function(c, rect) { var x = rect.x; var y = rect.y; var width = rect.w; var height = rect.h; var radius = rect.r; c.beginPath(); c.moveTo(x + radius, y); c.lineTo(x + width - radius, y); c.quadraticCurveTo(x + width, y, x + width, y + radius); c.lineTo(x + width, y + height - radius); c.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); c.lineTo(x + radius, y + height); c.quadraticCurveTo(x, y + height, x, y + height - radius); c.lineTo(x, y + radius); c.quadraticCurveTo(x, y, x + radius, y); c.closePath(); c.stroke(); c.fill(); }; 

index.html

 <script> $(function() { $("#directionalLight,#ambientLight").each(function() { new colorSlider($(this)); }); }); </script> <body> <div>Directional light</div> <div id="directionalLight"></div> <div>Ambient light</div> <div id="ambientLight"></div> </body> 
+4
source share
1 answer

The first thing to know is that the canvas element has internal dimensions = the number of pixels in the internal coordinate space (the width and height attributes and properties are set). It also has external dimensions ( style.width and style.height ), which is the number of pixels that an image takes on a web page. The inner pixels are scaled to fit the outer space.

This is confusing because img also has internal and external dimensions, but the property names are completely different from canvas . If you set width and height to an image, this is basically the same as setting style.width or style.height ; they both set external dimensions to scale the image on the page. Meanwhile, you can get the img internal dimensions using the new properties naturalWidth and naturalHeight (HTML5 only).

If external dimensions are not set on both img and canvas , the image will be laid out in the same size as the internal dimensions (i.e., the scaling factor will be 1).

Now, when you use the jQuery, $(canvas).width('310px') coincides with the $(canvas).css('310px') , which sets the external dimensions. You have to call $(canvas).prop('width', 310) or just set canvas.width = 310 to set the inner width.

+19
source

Source: https://habr.com/ru/post/1396536/


All Articles