- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我希望在下面的演示中指出算法方面的正确方向 http://sta.sh/muro/。还有它正在使用的 Canvas 工具 - 即它是绘制线条还是绘制许多弧线等
具体来说,我想模拟画笔转动,这会导致整个“画笔笔触”更粗。请参阅图像以了解我要模拟的画笔设置。
最终,我希望创建一个在转动时厚度会变化的画笔,就像下面的行为一样。
最佳答案
为此,您需要记录按下按钮时的鼠标点。然后,您需要检查每个线段以找到方向、线的长度以及该线段的归一化向量,以便您可以对鼠标样本进行过采样。
因此,如果您有一组从鼠标获取的点,则以下内容将获得所需的详细信息。
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 **/
关于javascript - 在 HTML5 Canvas 中实现画笔厚度变化(示例想要在内部模拟),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34823751/
背景: 我最近一直在使用 JPA,我为相当大的关系数据库项目生成持久层的轻松程度给我留下了深刻的印象。 我们公司使用大量非 SQL 数据库,特别是面向列的数据库。我对可能对这些数据库使用 JPA 有一
我已经在我的 maven pom 中添加了这些构建配置,因为我希望将 Apache Solr 依赖项与 Jar 捆绑在一起。否则我得到了 SolarServerException: ClassNotF
interface ITurtle { void Fight(); void EatPizza(); } interface ILeonardo : ITurtle {
我希望可用于 Java 的对象/关系映射 (ORM) 工具之一能够满足这些要求: 使用 JPA 或 native SQL 查询获取大量行并将其作为实体对象返回。 允许在行(实体)中进行迭代,并在对当前
好像没有,因为我有实现From for 的代码, 我可以转换 A到 B与 .into() , 但同样的事情不适用于 Vec .into()一个Vec . 要么我搞砸了阻止实现派生的事情,要么这不应该发
在 C# 中,如果 A 实现 IX 并且 B 继承自 A ,是否必然遵循 B 实现 IX?如果是,是因为 LSP 吗?之间有什么区别吗: 1. Interface IX; Class A : IX;
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我正在阅读标准haskell库的(^)的实现代码: (^) :: (Num a, Integral b) => a -> b -> a x0 ^ y0 | y0 a -> b ->a expo x0
我将把国际象棋游戏表示为 C++ 结构。我认为,最好的选择是树结构(因为在每个深度我们都有几个可能的移动)。 这是一个好的方法吗? struct TreeElement{ SomeMoveType
我正在为用户名数据库实现字符串匹配算法。我的方法采用现有的用户名数据库和用户想要的新用户名,然后检查用户名是否已被占用。如果采用该方法,则该方法应该返回带有数据库中未采用的数字的用户名。 例子: “贾
我正在尝试实现 Breadth-first search algorithm , 为了找到两个顶点之间的最短距离。我开发了一个 Queue 对象来保存和检索对象,并且我有一个二维数组来保存两个给定顶点
我目前正在 ika 中开发我的 Python 游戏,它使用 python 2.5 我决定为 AI 使用 A* 寻路。然而,我发现它对我的需要来说太慢了(3-4 个敌人可能会落后于游戏,但我想供应 4-
我正在寻找 Kademlia 的开源实现C/C++ 中的分布式哈希表。它必须是轻量级和跨平台的(win/linux/mac)。 它必须能够将信息发布到 DHT 并检索它。 最佳答案 OpenDHT是
我在一本书中读到这一行:-“当我们要求 C++ 实现运行程序时,它会通过调用此函数来实现。” 而且我想知道“C++ 实现”是什么意思或具体是什么。帮忙!? 最佳答案 “C++ 实现”是指编译器加上链接
我正在尝试使用分支定界的 C++ 实现这个背包问题。此网站上有一个 Java 版本:Implementing branch and bound for knapsack 我试图让我的 C++ 版本打印
在很多情况下,我需要在 C# 中访问合适的哈希算法,从重写 GetHashCode 到对数据执行快速比较/查找。 我发现 FNV 哈希是一种非常简单/好/快速的哈希算法。但是,我从未见过 C# 实现的
目录 LRU缓存替换策略 核心思想 不适用场景 算法基本实现 算法优化
1. 绪论 在前面文章中提到 空间直角坐标系相互转换 ,测绘坐标转换时,一般涉及到的情况是:两个直角坐标系的小角度转换。这个就是我们经常在测绘数据处理中,WGS-84坐标系、54北京坐标系
在软件开发过程中,有时候我们需要定时地检查数据库中的数据,并在发现新增数据时触发一个动作。为了实现这个需求,我们在 .Net 7 下进行一次简单的演示. PeriodicTimer .
二分查找 二分查找算法,说白了就是在有序的数组里面给予一个存在数组里面的值key,然后将其先和数组中间的比较,如果key大于中间值,进行下一次mid后面的比较,直到找到相等的,就可以得到它的位置。
我是一名优秀的程序员,十分优秀!