我想指出了正确的方向在算法方面在演示下面这里http://sta.sh/muro /。此外,它是使用画布的工具 - 即它被画线或绘图许多弧等实施刷厚度变化(例如要内仿真)
具体来说,我想模仿刷转动,将导致整个“刷行程”,以更厚。查看图像以了解我想要模拟的笔刷设置。
最终,我想一个创建涂料刷在导通时,酷似下面的行为,将在厚度上变化。
我想指出了正确的方向在算法方面在演示下面这里http://sta.sh/muro /。此外,它是使用画布的工具 - 即它被画线或绘图许多弧等实施刷厚度变化(例如要内仿真)
具体来说,我想模仿刷转动,将导致整个“刷行程”,以更厚。查看图像以了解我想要模拟的笔刷设置。
最终,我想一个创建涂料刷在导通时,酷似下面的行为,将在厚度上变化。
要做到这一点,你需要记录鼠标点时的按键时。然后,您需要检查每个线段以查找方向,线的长度和线段的归一化矢量,以便您可以过度采样鼠标样本。
所以,如果你有一组从鼠标取点以下将获得所需的细节。
for(var i = 0; i < len-1; i++){
var p1 = line[i];
var p2 = line[i+1];
var nx = p2.x - p1.x;
var ny = p2.y - p1.y;
p1.dir = ((Math.atan2(ny,nx)%PI2)+PI2)%PI2; // get direction
p1.len = Math.hypot(nx,ny); // get length
p1.nx = nx/p1.len; // get normalised vector
p1.ny = ny/p1.len;
}
一旦你有每条线段的细节是一件简单的事情的变化根据图纸值参数。
我已添加演示。它可能会让我很难接近这个例子,因为您提供的线路没有给出选项来绘制您的图像。图像显示他们也使用阴影,并进行子鼠标采样。他们绘制的圆形框可能是一个图像,比我绘制的框要快得多。我也平滑了这一行,所以在绘制和最终确定背景图像之间存在一点点滞后。
演示顶部是一组控制各种设置的常量。如果您发现代码有用,那么添加输入选项和审查将花费太多时间,因此请与他们一起玩。
对不起,代码是混乱的,但它只是一个例子,你将不得不把它分开自己。
/** hypot.js begin **/
// ES6 new math function hypot. Sqrt of the sum of the squares
var hypot = Math.hypot;
if(typeof hypot === 'undefined'){
hypot = function(x,y){
return Math.sqrt(Math.pow(x,2)+Math.pow(y,2));
}
}
/** hypot.js end **/
// draw options
const SUB_SECTIONS = 5; // points between mouse samples
const SIZE_MULT = 3; // Max size multiplier
const SIZE_MIN = 0.1 // min size of line
const BIG_DIR = 0.6; // direction in radians for thickest line
const SMOOTH_MAX = 7; // number of smoothing steps performed on a line. Bigger that 20 will slow the rendering down
const SHAPE_ALPHA = 0.5; // the stoke alpha
const SHAPE_FILL_ALPHA = 0.75; // the fill alpha
const SHADOW_ALPHA = 0.1; // the shadow alpha
const SHADOW_BLUR = 5; // the shadow blur
const SHADOW_OFFX = 6; // shoadow offest x and y
const SHADOW_OFFY = 6;
const SHAPE_LINE_WIDTH = 0.6; // stroke width of shape. This is constant and is not scaled
const SHAPE_WIDTH = 4; // shape drawn width;
const SHAPE_LENGTH = 20; // shape drawn length
const SHAPE_ROUNDING = 2; // shape rounded corner radius. Warning invalid results if rounding is greater than half width or height which ever is the smallest
const SHAPE_TRAIL = 0; // offset draw shape. Negivive numbers trail drawing positive are infront
var div = document.createElement("div");
div.textContent = "Click drag mouse to draw, Right click to clear."
document.body.appendChild(div);
var mouse;
var demo = function(){
/** fullScreenCanvas.js begin **/
var canvas = (function(){
var canvas = document.getElementById("canv");
if(canvas !== null){
document.body.removeChild(canvas);
}
// creates a blank image with 2d context
canvas = document.createElement("canvas");
canvas.id = "canv";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
canvas.style.zIndex = 1000;
canvas.ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
return canvas;
})();
var ctx = canvas.ctx;
/** fullScreenCanvas.js end **/
/** MouseFull.js begin **/
if(typeof mouse !== "undefined"){ // if the mouse exists
if(mouse.removeMouse !== undefined){
mouse.removeMouse(); // remove previouse events
}
}
var canvasMouseCallBack = undefined; // if needed
mouse = (function(){
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false,
interfaceId : 0, buttonLastRaw : 0, buttonRaw : 0,
over : false, // mouse is over the element
bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
getInterfaceId : function() { return this.interfaceId++; }, // For UI functions
startMouse:undefined,
mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
function mouseMove(e) {
var t = e.type, m = mouse;
m.x = e.offsetX; m.y = e.offsetY;
if (m.x === undefined) { m.x = e.clientX; m.y = e.clientY; }
m.alt = e.altKey;m.shift = e.shiftKey;m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1];
} else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2];
} else if (t === "mouseout") { m.buttonRaw = 0; m.over = false;
} else if (t === "mouseover") { m.over = true;
} else if (t === "mousewheel") { m.w = e.wheelDelta;
} else if (t === "DOMMouseScroll") { m.w = -e.detail;}
if (canvasMouseCallBack) { canvasMouseCallBack(mouse); }
e.preventDefault();
}
function startMouse(element){
if(element === undefined){
element = document;
}
mouse.element = element;
mouse.mouseEvents.forEach(
function(n){
element.addEventListener(n, mouseMove);
}
);
element.addEventListener("contextmenu", function (e) {e.preventDefault();}, false);
}
mouse.removeMouse = function(){
if(mouse.element !== undefined){
mouse.mouseEvents.forEach(
function(n){
mouse.element.removeEventListener(n, mouseMove);
}
);
canvasMouseCallBack = undefined;
}
}
mouse.mouseStart = startMouse;
return mouse;
})();
if(typeof canvas !== "undefined"){
mouse.mouseStart(canvas);
}else{
mouse.mouseStart();
}
/** MouseFull.js end **/
/** CreateImage.js begin **/
// creates a blank image with 2d context
var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}
/** CreateImage.js end **/
/** FrameUpdate.js begin **/
var w = canvas.width;
var h = canvas.height;
var cw = w/2;
var ch = h/2;
var line = []; // line to hold drawing points
var image = createImage(w,h); // Background image to dump point to when soothed
var PI2 = Math.PI * 2; // 360 to save typing
var PIh = Math.PI/2; // 90
// draws a rounded rectangle path
function roundedRect(ctx,x, y, w, h, r){
ctx.beginPath();
ctx.arc(x + r, y + r, r, PIh * 2, PIh * 3);
ctx.arc(x + w - r, y + r, r, PIh * 3, PI2);
ctx.arc(x + w - r, y + h - r, r, 0, PIh);
ctx.arc(x + r, y + h - r, r, PIh, PIh * 2);
ctx.closePath();
}
// this draws a section of line
function drawStroke(ctx,line){
var len = line.length;
ctx.shadowBlur = SHADOW_BLUR;
ctx.shadowOffsetX = SHADOW_OFFX;
ctx.shadowOffsetY = SHADOW_OFFY;
ctx.shadowColor = "rgba(0,0,0," + SHADOW_ALPHA + ")";
ctx.strokeStyle = "rgba(0,0,0," + SHAPE_FILL_ALPHA + ")";
ctx.fillStyle = "rgba(255,255,255," + SHAPE_ALPHA + ")";
for (var i = 0; i < len - 1; i++) { // for each point minus 1
var p1 = line[i];
var p2 = line[i + 1]; // get the point and one ahead
if (p1.dir && p2.dir) { // do both points have a direction
// divide the distance between the points by 5 and draw each sub section
for (var k = 0; k < p1.len; k += p1.len/SUB_SECTIONS) {
// get the points between mouse samples
var x = p1.x + p1.nx * k;
var y = p1.y + p1.ny * k;
var kk = k/p1.len; // get normalised distance
// tween direction but need to check cyclic
if (p1.dir > Math.PI * 1.5 && p2.dir < Math.PI/2) {
var dir = ((p2.dir + Math.PI * 2) - p1.dir) * kk + p1.dir;
} else
if (p2.dir > Math.PI * 1.5 && p1.dir < Math.PI/2) {
var dir = ((p2.dir - Math.PI * 2) - p1.dir) * kk + p1.dir;
} else {
var dir = (p2.dir - p1.dir) * kk + p1.dir;
}
// get size dependent on direction
var size = (Math.abs(Math.sin(dir + BIG_DIR)) + SIZE_MIN) * SIZE_MULT;
// caculate the transform requiered.
var xdx = Math.cos(dir) * size;
var xdy = Math.sin(dir) * size;
// set the line width to the invers scale so it remains constant
ctx.lineWidth = SHAPE_LINE_WIDTH * (1/size); // make sure that the line width does not scale
// set the transform
ctx.setTransform(xdx, xdy, -xdy, xdx, x, y);
// draw the shape
roundedRect(ctx, -SHAPE_LENGTH/2 - SHAPE_TRAIL, -SHAPE_WIDTH/2, SHAPE_LENGTH, SHAPE_WIDTH, SHAPE_ROUNDING);
// fill and stroke
ctx.fill();
ctx.stroke();
}
}
}
// restore transform
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
// update function will try 60fps but setting will slow this down.
function update(){
// restore transform
ctx.setTransform(1, 0, 0, 1, 0, 0);
// clear
ctx.clearRect(0, 0, w, h);
// get line length
var len = line.length;
if (mouse.buttonRaw !== 1) { // button up so draw all onto image
drawStroke(image.ctx, line)
line = [];
} else {
// remove trailing line segments that are no longer being smoothed
if (len > SMOOTH_MAX * 2) {
var a = line.splice(0, SMOOTH_MAX - 1)
a.push(line[0]);
drawStroke(image.ctx, a)
}
}
// draw background image
ctx.drawImage(image, 0, 0);
// is the button down
if (mouse.buttonRaw === 1) {
// if more than one point
if (line.length > 0) {
// only add a point if mouse has moved.
if (mouse.x !== line[line.length - 1].x || mouse.y !== line[line.length - 1].y) {
line.push({
x : mouse.x,
y : mouse.y,
s : 0
});
}
} else {
// add a point if no points exist
line.push({
x : mouse.x,
y : mouse.y,
s : 0
});
}
}
// get number of points
var len = line.length;
if(mouse.buttonRaw === 1){ // mouse down the do simple running average smooth
// This smooth will continue to refine points untill the it is outside the
// smoothing range/
for (var i = 0; i < len - 3; i++) {
var p1 = line[i];
var p2 = line[i + 1];
var p3 = line[i + 2];
if (p1.s < SMOOTH_MAX) {
p1.s += 1;
p2.x = ((p1.x + p3.x)/2 + p2.x * 2)/3;
p2.y = ((p1.y + p3.y)/2 + p2.y * 2)/3;
}
}
// caculate the direction, length and normalised vector for
// each line segment and add to the point
for(var i = 0; i < len-1; i++){
var p1 = line[i];
var p2 = line[i + 1];
var nx = p2.x - p1.x;
var ny = p2.y - p1.y;
p1.dir = ((Math.atan2(ny, nx) % PI2) + PI2) % PI2; // get direction
p1.len = hypot(nx, ny); // get length
p1.nx = nx/p1.len; // get normalised vector
p1.ny = ny/p1.len;
}
// draw the line points onto the canvas.
drawStroke(ctx,line)
}
if((mouse.buttonRaw & 4)=== 4){
line = [];
image.ctx.clearRect(0,0,w,h);
ctx.clearRect(0,0,w,h);
mouse.buttonRaw = 0;
}
if(!STOP){
requestAnimationFrame(update);
}else{
var can = document.getElementById("canv");
if(can !== null){
document.body.removeChild(can);
}
STOP = false;
}
}
update();
}
var STOP = false; // flag to tell demo app to stop
function resizeEvent(){
var waitForStopped = function(){
if(!STOP){ // wait for stop to return to false
demo();
return;
}
setTimeout(waitForStopped,200);
}
STOP = true;
setTimeout(waitForStopped,100);
}
window.addEventListener("resize",resizeEvent);
demo();
/** FrameUpdate.js end **/
这是非常有益的,正是我一直在寻找 - 找不到anythign像这样的任何地方。非常感谢你。 – eirikrl