I'm implementing a color picker. There is problem with the rendering. When I call c.fillRect(0, 0, 100, 80);
the size of that rectangle is 103x42 px instead of 100x80. What is wrong here?
Also, rectangles are antialiased. Do I need offset the position by (0.5, 0.5) to avoid AA? I didn't use any kind of the 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>
Click the canvas border handles to resize the canvas dynamically. When you drag the border handles without using any keyboard modifiers, the canvas resizes non-uniformly. Hold Shift to constrain the resize proportions. Hold Alt to resize proportionally toward the center of the canvas.
A canvas element has a size in pixels, which applies to the bitmap you draw onto, and it has a CSS size like any other HTML element. These two are not the same (exactly like how a JPEG has a size in pixels but can be displayed at any size on screen), but you can set either one at any time.
The clearRect() method in HTML canvas is used to clear the pixels in a given rectangle. The <canvas> element allows you to draw graphics on a web page using JavaScript. Every canvas has two elements that describes the height and width of the canvas i.e. height and width respectively. ctx.
To draw the rectangle onto a canvas, you can use the fill() or stroke() methods. Note: To both create and render a rectangle in one step, use the fillRect() or strokeRect() methods.
The first thing to know is that a canvas
element has intrinsic dimensions = number of pixels in the inside coordinate space (set by the width
and height
attributes and properties). It also has extrinsic dimensions (style.width
and style.height
) which is the number of pixels that the image takes within the webpage. The intrinsic pixels are scaled to fit the extrinsic space.
It's confusing because an img
also has intrinsic and extrinsic dimensions, but the names of the properties are completely different from canvas
. If you set width
and height
on an image, it's basically the same as setting style.width
or style.height
; they both set the extrinsic dimensions to scale the image within the page. Meanwhile, you can only get the intrinsic dimensions of an img
using the new naturalWidth
and naturalHeight
(HTML5 browsers only) properties.
If the extrinsic dimensions are not set on both img
and canvas
, the image will be laid out at the same size as the intrinsic dimensions (i.e., scale factor would be 1).
Now, when you use jQuery, $(canvas).width('310px')
is the same as $(canvas).css('310px')
, which sets the extrinsic dimensions. You have to call $(canvas).prop('width', 310)
or simply set canvas.width = 310
to set the intrinsic width.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With