Javascript HTML5画布中的手绘圆模拟
使用jQuery在HTML 5画布中创建一个圆: 代码:Javascript HTML5画布中的手绘圆模拟,javascript,jquery,canvas,html5-canvas,Javascript,Jquery,Canvas,Html5 Canvas,使用jQuery在HTML 5画布中创建一个圆: 代码: //get a reference to the canvas var ctx = $('#canvas')[0].getContext("2d"); DrawCircle(75, 75, 20); //draw a circle function DrawCircle(x, y, radius) { ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI*2, true);
//get a reference to the canvas
var ctx = $('#canvas')[0].getContext("2d");
DrawCircle(75, 75, 20);
//draw a circle
function DrawCircle(x, y, radius)
{
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI*2, true);
ctx.fillStyle = 'transparent';
ctx.lineWidth = 2;
ctx.strokeStyle = '#003300';
ctx.stroke();
ctx.closePath();
ctx.fill();
}
我尝试模拟以下任何类型的圆:
我已经研究并发现了这个,但无法应用它
我想画一个圆圈,而不是仅仅出现
有更好的方法吗?我感觉会涉及很多数学问题:)
另外,我喜欢的简单,也许这是使用其简化路径的最简单方法?您的任务似乎有3个要求:
- 亲手画一个圆圈
- 将构成圆的每个线段保存在一个数组中
- 使用安德鲁风格化的画笔技术“播放”这些片段
你有一个有趣的项目…如果你觉得慷慨,请分享你的结果 以下是我为这个答案创建的一些基础知识: 基本上,当你画一个圆时,你需要考虑手的缺陷。因此,在以下代码中:
var img = new Image();
img.src="data:image/png;base64,...";
var ctx = $('#sketch')[0].getContext('2d');
function draw(x,y) {
ctx.drawImage(img, x, y);
}
for (var i=0; i<500; i++) {
var radiusError = +10 - i/20;
var d = 2*Math.PI/360 * i;
draw(200 + 100*Math.cos(d), 200 + (radiusError+80)*Math.sin(d) );
}
显然,圆心位于(200200),因为用三角函数绘制圆的公式是
x = centerX + RX * cos ( angle )
y = centerY + RY * sin ( angle )
这里已经有了很好的解决方案。我想添加一个已经呈现的变体-如果想要模拟手绘的圆,除了一些三角学之外没有太多的选项 我首先建议实际记录一个真实的手绘圆圈。您可以记录这些点以及
时间戳
,并在以后的任何时候复制精确的图形。您可以将其与线平滑算法相结合
此解决方案在此处生成如下圆圈:
您可以像往常一样通过设置笔划样式
、线宽
等来更改颜色、厚度等
要画一个圆圈,只需调用:
handDrawCircle(context, x, y, radius [, rounds] [, callback]);
(回调
是在动画使函数异步时提供的)
代码分为两部分:
function handDrawCircle(ctx, cx, cy, r, rounds, callback) {
/// rounds is optional, defaults to 3 rounds
rounds = rounds ? rounds : 3;
var x, y, /// the calced point
tol = Math.random() * (r * 0.03) + (r * 0.025), ///tolerance / fluctation
dx = Math.random() * tol * 0.75, /// "bouncer" values
dy = Math.random() * tol * 0.75,
ix = (Math.random() - 1) * (r * 0.0044), /// speed /incremental
iy = (Math.random() - 1) * (r * 0.0033),
rx = r + Math.random() * tol, /// radius X
ry = (r + Math.random() * tol) * 0.8, /// radius Y
a = 0, /// angle
ad = 3, /// angle delta (resolution)
i = 0, /// counter
start = Math.random() + 50, /// random delta start
tot = 360 * rounds + Math.random() * 50 - 100, /// end angle
points = [], /// the points array
deg2rad = Math.PI / 180; /// degrees to radians
在主循环中,我们不会随机反弹,而是使用随机值递增,然后使用该值线性递增,如果处于边界(公差),则反转该值
提示:要获得更真实的笔划,您可以将globalAlpha
减少到例如0.7
但是,要使其正常工作,您需要先将实体绘制到屏幕外画布,然后将屏幕外画布blit到主画布(设置了globalAlpha
),否则笔划将在每个点之间重叠(看起来不太好)
对于正方形,可以使用与圆相同的方法,但不使用半径和角度,而是将变化应用于直线。偏移增量以使直线不直
我稍微调整了值,但可以随意调整它们以获得更好的结果
要使圆稍微“倾斜”,可以先稍微旋转画布:
rotate = Math.random() * 0.5;
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(-rotate);
ctx.translate(-cx, -cy);
循环结束时:
if (i < points.length) {
requestAnimationFrame(draw);
} else {
ctx.restore();
}
然后当动画完成时:
if (i < points.length) {
requestAnimationFrame(draw);
} else {
ctx.restore();
if (typeof callback === 'function')
callback(); /// call next function
}
if(i
您将遇到的另一个问题是代码本身(请记住,代码只是一个示例,而不是完整的解决方案:-)),这是一个粗线条:
当我们单独逐段绘制时,canvas不知道如何计算直线相对于前一段的对接角度。这是路径概念的一部分。当您使用多个线段绘制路径时,画布将知道对接(线的端点)的角度。因此,这里我们要么从起点到当前点画一条线,并在中间进行清除,要么只画一个较小的线宽值
当我们使用<代码> CurrCutt < /C> >(这将使线条平滑而不是“锯齿状”,因为当我们不使用一个透明的,但只是在上面画上)时,我们需要考虑实现一个顶部的画布来完成动画,当动画完成时,我们将结果绘制到主画布上。
现在我们开始看到其中的“复杂性”。这当然是因为canvas是“低级的”,即我们需要为所有事物提供所有逻辑。我们基本上是在每次使用画布做更多的事情时构建系统,而不仅仅是绘制简单的形状和图像(但这也提供了极大的灵活性)。
也可以作为
功能路径(δr_min,δr_max,el0_min,el0_max,δel_min,δel_max){
var c=0.551915024494;
var atan=Math.atan(c)
VarD=Math.sqrt(c*c+1*1),r=1;
var el=(el0_min+Math.random()*(el0_max-el0_min))*Math.PI/180;
var路径='M';
路径+=[r*Math.sin(el
rotate = Math.random() * 0.5;
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(-rotate);
ctx.translate(-cx, -cy);
if (i < points.length) {
requestAnimationFrame(draw);
} else {
ctx.restore();
}
handDrawCircle(context, x, y, radius [, rounds] [, callback]);
if (i < points.length) {
requestAnimationFrame(draw);
} else {
ctx.restore();
if (typeof callback === 'function')
callback(); /// call next function
}
<div id="container">
<svg width="100%" height="100%" viewBox='-1.5 -1.5 3 3'></svg>
</div>
#container {
width:500px;
height:300px;
}
path.ln {
stroke-width: 3px;
stroke: #666;
fill: none;
vector-effect: non-scaling-stroke;
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
-webkit-animation: dash 5s ease-in forwards;
-moz-animation:dash 5s ease-in forwards;
-o-animation:dash 5s ease-in forwards;
animation:dash 5s ease-in forwards;
}
@keyframes dash {
to { stroke-dashoffset: 0; }
}
function path(δr_min,δr_max, el0_min, el0_max, δel_min,δel_max) {
var c = 0.551915024494;
var atan = Math.atan(c)
var d = Math.sqrt( c * c + 1 * 1 ), r = 1;
var el = (el0_min + Math.random() * (el0_max - el0_min)) * Math.PI / 180;
var path = 'M';
path += [r * Math.sin(el), r * Math.cos(el)];
path += ' C' + [d * r * Math.sin(el + atan), d * r * Math.cos(el + atan)];
for (var i = 0; i < 4; i++) {
el += Math.PI / 2 * (1 + δel_min + Math.random() * (δel_max - δel_min));
r *= (1 + δr_min + Math.random()*(δr_max - δr_min));
path += ' ' + (i?'S':'') + [d * r * Math.sin(el - atan), d * r * Math.cos(el - atan)];
path += ' ' + [r * Math.sin(el), r * Math.cos(el)];
}
return path;
}
function cX(λ_min, λ_max, el_min, el_max) {
var el = (el_min + Math.random()*(el_max - el_min));
return 'rotate(' + el + ') ' + 'scale(1, ' + (λ_min + Math.random()*(λ_max - λ_min)) + ')'+ 'rotate(' + (-el) + ')';
}
function canvasArea() {
var width = Math.floor((Math.random() * 500) + 450);
var height = Math.floor((Math.random() * 300) + 250);
$('#container').width(width).height(height);
}
d3.selectAll( 'svg' ).append( 'path' ).classed( 'ln', true) .attr( 'd', path(-0.1,0, 0,360, 0,0.2 )).attr( 'transform', cX( 0.6, 0.8, 0, 360 ));
setTimeout(function() { location = '' } ,5000)