我正在努力做一些复杂的效果,为了做到这一点,我必须把它分解成它的组件,我可以在此基础上,希望它们会聚集在一起。
现在在画布上制作圆圈很容易。但我想自己做。所以我想编写一个函数,给出一个点为中心,半径,然后它将绘制一个1 px笔画宽度的圆。
我该怎么办?如果我从数学角度来看,我想到的是使用圆距离公式并按小值增加,如.3度,并在圆周上做一个点。但是如果我的圆圈太小,比如2 px半径。然后它将浪费大量时间绘制不重要的东西,如果它足够大,你会看到点之间的空间。
所以我想要我的圆绘图功能来绘制
我想,一旦我知道如何使轮廓填充它就不会成为一个问题......我要做的就是缩小半径并保持绘制直到半径为1px。 / p>
答案 0 :(得分:0)
您的中心为x0, y0
,半径为r
。基本上你需要圆的参数方程:
x = x0 + r * cos(t)
y = y0 + r * sin(t)
其中t
是径向段和标准化x轴之间的角度,您需要根据需要将其分开。例如,对于您的四点情况,您可以
360/4 = 90
所以使用0,90,180,270得到四个点。
答案 1 :(得分:0)
好的,我已将我之前的代码重新计算为名为" canvasLens"的jQuery插件。它接受一系列选项来控制图像src,镜头大小和边框颜色等内容。您甚至可以在两种不同的镜头效果之间进行选择," fisheye"或" scaledSquare"。
我试图通过标题栏和其他许多评论尽可能地使其变得不言自明。
/*
* Copyright (c) 2014 Roamer-1888
* "canvasLens"
* a jQuery plugin for a lens effect on one or more HTML5 canvases
* by Roamer-1888, 2014-11-09
* http://stackoverflow.com/users/3478010/roamer-1888
*
* Written in response to aa question by Muhammad Umer, here
* http://stackoverflow.com/questions/26793321/
*
* Invoke on a canvas element as follows
* $("#canvas").lens({
* imgSrc: 'path/to/image',
* imgCrossOrigin: '' | 'anonymous' | 'use-credentials', //[1]
* drawImageCoords: [ //[2]
* 0, 0, //(sx,st) Source image sub-rectangle Left,Top.
* 1350, 788, //(sw/sh) Source image sub-rectangle Width,Height.
* 0, 0, //(dx/dy) Destination Left,Top.
* 800, 467 //(dw/dh) Destination image sub-rectangle Width,Height.
* ],
* effect: 'fisheye' | 'scaledSquare',
* scale: 2 //currently affects only 'scaledSquare'
* size: 100, //diameter/side-length of the lens in pixels
* hideCursor: true | false,
* border: [0, 0, 0, 255] //[r,g,b,alpha] (base-10) | 'none'
* });
*
* Demo: http://jsfiddle.net/7z6by3o3/1/
*
* Further reading :
* [1] imgCrossOrigin -
* https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes
* [2] drawImageCoords -
* https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D
*
* Licence: MIT - http://en.wikipedia.org/wiki/MIT_License
*
* Please keep this header block intact, with amendments
* to reflect any changes made to the code.
*
*/
(function($){
// *******************************
// ***** Start: Private vars *****
// *******************************
var pluginName = 'canvasLens';
// *****************************
// ***** Fin: Private vars *****
// *****************************
// **********************************
// ***** Start: Private Methods *****
// **********************************
// Note that in all private methods,
// `this` is the canvas on which
// the plugin is invoked.
// Most private methods are called
// with `methodName.call(this)`.
// **********************************
function animate() {
var data = $(this).data(pluginName);
if(data) {
draw.call(this);
requestAnimationFrame(animate.bind(this));
}
}
function draw() {
var data = $(this).data(pluginName);
data.ctx.drawImage(data.m_can, 0, 0);
if(data.showLens) {
if(data.settings.effect == 'scaledSquare') {
scaledSquare.call(this);
} else {
fisheye.call(this);
}
}
}
function putBg() {
var data = $(this).data(pluginName);
data.m_ctx.drawImage.apply(data.m_ctx, [data.img].concat(data.settings.drawImageCoords));
}
function scaledSquare() {
var data = $(this).data(pluginName),
xt = data.settings.scale,
h = data.settings.size;
data.ctx.drawImage(data.m_can,
data.mouse.x - h/xt/2, data.mouse.y - h/xt/2, //sx,st Source image sub-rectangle Left,Top coordinates.
h/xt, h/xt, //sw/sh Source image sub-rectangle Width,Height.
data.mouse.x - h/2, data.mouse.y - h/2, //dx/dy Destination Left,Top coordinates.
h, h //dw/dh The Width,Height to draw the image in the destination canvas.
);
}
function fisheye() {
var data = $(this).data(pluginName),
d = data.settings.size,
mx = data.mouse.x, my = data.mouse.y,
srcpixels = data.m_ctx.getImageData(mx - d/2, my - d/2, d, d);
fisheyeTransform.call(this, srcpixels.data, data.xpixels.data, d, d);
data.ctx.putImageData(data.xpixels, mx - d/2, my - d/2);
}
function fisheyeTransform(srcData, xData, w, h) {
/*
* Fish eye effect (barrel distortion)
* *** adapted from ***
* tejopa, 2012-04-29
* http://popscan.blogspot.co.ke/2012/04/fisheye-lens-equation-simple-fisheye.html
*/
var data = $(this).data(pluginName),
y, x, ny, nx, ny2, nx2, r, nr, theta, nxn, nyn, x2, y2, pos, srcpos;
for (var y=0; y<h; y++) { // for each row
var ny = ((2 * y) / h) - 1; // normalize y coordinate to -1 ... 1
ny2 = ny * ny; // pre calculate ny*ny
for (x=0; x<w; x++) { // for each column
pos = 4 * (y * w + x);
nx = ((2 * x) / w) - 1; // normalize x coordinate to -1 ... 1
nx2 = nx * nx; // pre calculate nx*nx
r = Math.sqrt(nx2 + ny2); // calculate distance from center (0,0)
if(r > 1) {
/* 1-to-1 pixel mapping outside the circle */
/* An improvement would be to make this area transparent. ?How? */
xData[pos+0] = srcData[pos+0];//red
xData[pos+1] = srcData[pos+1];//green
xData[pos+2] = srcData[pos+2];//blue
xData[pos+3] = srcData[pos+3];//alpha
}
else if(data.settings.border && data.settings.border !== 'none' && r > (1-3/w) && r < 1) { // circular border around fisheye
xData[pos+0] = data.settings.border[0];//red
xData[pos+1] = data.settings.border[1];//green
xData[pos+2] = data.settings.border[2];//blue
xData[pos+3] = data.settings.border[3];//alpha
}
else if (0<=r && r<=1) { // we are inside the circle, let's do a fisheye transform on this pixel
nr = Math.sqrt(1 - Math.pow(r,2));
nr = (r + (1 - nr)) / 2; // new distance is between 0 ... 1
if (nr<=1) { // discard radius greater than 1.0
theta = Math.atan2(ny, nx); // calculate the angle for polar coordinates
nxn = nr * Math.cos(theta); // calculate new x position with new distance in same angle
nyn = nr * Math.sin(theta); // calculate new y position with new distance in same angle
x2 = Math.floor(((nxn + 1) * w) / 2); // map from -1 ... 1 to image coordinates
y2 = Math.floor(((nyn + 1) * h) / 2); // map from -1 ... 1 to image coordinates
srcpos = Math.floor(4 * (y2 * w + x2));
if (pos >= 0 && srcpos >= 0 && (pos+3) < xData.length && (srcpos+3) < srcData.length) { // make sure that position stays within arrays
/* get new pixel (x2,y2) and put it to target array at (x,y) */
xData[pos+0] = srcData[srcpos+0];//red
xData[pos+1] = srcData[srcpos+1];//green
xData[pos+2] = srcData[srcpos+2];//blue
xData[pos+3] = srcData[srcpos+3];//alpha
}
}
}
}
}
}
// ********************************
// ***** Fin: Private methods *****
// ********************************
// *********************************
// ***** Start: Public Methods *****
// *********************************
var methods = {
'init': function(options) {
//"this" is a jquery object on which this plugin has been invoked.
return this.each(function(index) {
var can = this,
$this = $(this);
var data = $this.data(pluginName);
if (!data) { // If the plugin hasn't been initialized yet
data = {
target: $this,
showLens: false,
mouse: {x:0, y:0}
};
$this.data(pluginName, data);
var settings = {
imgSrc: '',
imgCrossOrigin: '',
drawImageCoords: [
0, 0, //sx,st Source image sub-rectangle Left,Top coordinates.
500, 500, //sw/sh Source image sub-rectangle Width,Height.
0, 0, //dx/dy Destination Left,Top coordinates.
500, 500 //(dw/dh) Destination image sub-rectangle Width,Height.
],
effect: 'fisheye',
scale: 2,
size: 100,
border: [0, 0, 0, 255], //[r,g,b,alpha] base-10
hideCursor: false
};
if(options) {
$.extend(true, settings, options);
}
data.settings = settings;
if(settings.hideCursor) {
data.originalCursor = $this.css('cursor');
$this.css('cursor', 'none');
}
$this.on('mouseenter.'+pluginName, function(e) {
data.showLens = true;
}).on('mousemove.'+pluginName, function(e) {
data.mouse.x = e.offsetX;
data.mouse.y = e.offsetY;
}).on('mouseleave.'+pluginName, function(e) {
data.showLens = false;
});
data.m_can = $("<canvas>").attr({
'width': can.width,
'height': can.height
})[0];
data.ctx = can.getContext("2d"); // lens effect
data.m_ctx = data.m_can.getContext('2d'); // background image
data.xpixels = data.ctx.getImageData(0, 0, settings.size, settings.size);
data.img = new Image();
data.img.onload = function() {
putBg.call(can);
animate.call(can);
};
data.img.crossOrigin = settings.imgCrossOrigin;
data.img.src = settings.imgSrc;
}
});
},
'destroy': function() {
return this.each(function(index) {
var $this = $(this),
data = $this.data(pluginName);
$this.off('mouseenter.'+pluginName)
.off('mousemove.'+pluginName)
.off('mouseleave.'+pluginName);
if(data && data.originalCursor) {
$this.css('cursor', data.originalCursor);
}
$this.data(pluginName, null);
});
}
};
// *******************************
// ***** Fin: Public Methods *****
// *******************************
// *****************************
// ***** Start: Supervisor *****
// *****************************
$.fn[pluginName] = function( method ) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || !method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist in jQuery.' + pluginName );
}
};
// ***************************
// ***** Fin: Supervisor *****
// ***************************
})(jQuery);
这是一个 Demo 。
修改强>
这是尝试解释鱼眼(桶形失真)计算......
w x h
像素的空白镜头开始。(nr, theta)
来机械化,然后应用标准数学公式,将极坐标转换为直角坐标nxn = nr * Math.cos(theta)
和nxn = nr * Math.sin(theta)
。到目前为止,所有计算都是在标准化的-1 ... 0 ... 1空间中进行的。