为什么在 Canvas 中的绘画应用程序中使用了 beginPath 方法?
Why beginPath method used in the paint application in Canvas?
这是关于 Canvas
的问题。这是一个用 rust
编写的示例绘画应用程序,可编译为 WebAssembly
。它使用 canvas 进行绘图。当鼠标移动并在 mouseUp
停止时,它在 mouseDown
事件开始用铅笔绘图。 Here 是 运行.
在 mouseMove
事件中,示例具有函数
context.line_to(event.offset_x() as f64, event.offset_y() as f64);
context.stroke();
context.begin_path(); //whats the use of this?
context.move_to(event.offset_x() as f64, event.offset_y() as f64); //whats the use of this?
为什么我们有最后两个 begin_path
和 move_to
函数调用? line_to
随着鼠标的移动,已经从头到尾画了一条线,然后从尾到下一点画了一条线。 begin_path
和 move_to
有什么用?
#[wasm_bindgen]
pub fn greet() -> Result<(), JsValue> {
utils::set_panic_hook();
let document = web_sys::window().unwrap().document().unwrap();
let canvas = document
.create_element("canvas")?
.dyn_into::<web_sys::HtmlCanvasElement>()?;
document.body().unwrap().append_child(&canvas)?;
canvas.set_width(640);
canvas.set_height(480);
canvas.style().set_property("border", "solid")?;
let context = canvas
.get_context("2d")?
.unwrap()
.dyn_into::<web_sys::CanvasRenderingContext2d>()?;
let context = Rc::new(context);
let pressed = Rc::new(Cell::new(false));
{
let context = context.clone();
let pressed = pressed.clone();
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
context.begin_path();
context.move_to(event.offset_x() as f64, event.offset_y() as f64);
pressed.set(true);
}) as Box<dyn FnMut(_)>);
canvas.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())?;
closure.forget();
}
{
let context = context.clone();
let pressed = pressed.clone();
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
if pressed.get() {
context.line_to(event.offset_x() as f64, event.offset_y() as f64);
context.stroke();
context.begin_path();
context.move_to(event.offset_x() as f64, event.offset_y() as f64);
}
}) as Box<dyn FnMut(_)>);
canvas.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())?;
closure.forget();
}
{
let context = context.clone();
let pressed = pressed.clone();
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
pressed.set(false);
context.line_to(event.offset_x() as f64, event.offset_y() as f64);
context.stroke();
}) as Box<dyn FnMut(_)>);
canvas.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())?;
closure.forget();
}
Ok(())
}
它是为了每次鼠标移动画一条线。
未能调用 beginPath()
,上下文的子路径仍将包含所有先前的绘制调用。因此,下次调用 stroke()
时,所有这些子路径都将被绘制,再次 ,覆盖之前的像素,从而产生丑陋的伪像。
然后调用moveTo
只是将未来的小段初始化为下一个鼠标移动向量。
这是一个示例,其中 strokeStyle
每次新的鼠标移动都会更改。由于我们不调用 beginPath()
,整个路径的颜色都发生了变化,而不仅仅是最后一段:
const canvas = document.querySelector( "canvas" );
const ctx = canvas.getContext("2d");
var hue = 0;
canvas.onmousemove = (evt) => {
const rect = canvas.getBoundingClientRect();
draw(evt.clientX - rect.left, evt.clientY - rect.top);
};
function draw(x, y) {
ctx.lineTo( x, y );
hue += 5;
ctx.strokeStyle = `hsl(${hue}deg,100%,50%)`;
ctx.stroke();
}
canvas {
border: 1px solid;
}
<canvas></canvas>
相比之下,调用 beginPath
时会发生以下情况:
const canvas = document.querySelector( "canvas" );
const ctx = canvas.getContext("2d");
var hue = 0;
canvas.onmousemove = (evt) => {
const rect = canvas.getBoundingClientRect();
draw(evt.clientX - rect.left, evt.clientY - rect.top);
};
function draw(x, y) {
ctx.lineTo( x, y );
hue += 5;
ctx.strokeStyle = `hsl(${hue}deg,100%,50%)`;
ctx.stroke();
ctx.beginPath();
ctx.moveTo( x, y );
}
canvas {
border: 1px solid;
}
<canvas></canvas>
但是,这可能会在线路交汇处产生问题,因为实际上没有接头。
因此,更好的方法是将所有向量存储在一个数组中,每帧清除整个上下文,并重绘完整路径:(抱歉,仍然在 JS 中,我的生锈是 rusted 不存在).
const canvas = document.querySelector( "canvas" );
const ctx = canvas.getContext("2d");
const vectors = [];
canvas.onmousemove = (evt) => {
const rect = canvas.getBoundingClientRect();
draw(evt.clientX - rect.left, evt.clientY - rect.top);
};
function draw(x, y) {
vectors.push( { x, y } );
ctx.clearRect( 0, 0, canvas.width, canvas.height );
ctx.beginPath();
vectors.forEach( ({x, y}) => ctx.lineTo( x, y ) );
ctx.stroke();
}
canvas {
border: 1px solid;
}
<canvas></canvas>
这是关于 Canvas
的问题。这是一个用 rust
编写的示例绘画应用程序,可编译为 WebAssembly
。它使用 canvas 进行绘图。当鼠标移动并在 mouseUp
停止时,它在 mouseDown
事件开始用铅笔绘图。 Here 是 运行.
在 mouseMove
事件中,示例具有函数
context.line_to(event.offset_x() as f64, event.offset_y() as f64);
context.stroke();
context.begin_path(); //whats the use of this?
context.move_to(event.offset_x() as f64, event.offset_y() as f64); //whats the use of this?
为什么我们有最后两个 begin_path
和 move_to
函数调用? line_to
随着鼠标的移动,已经从头到尾画了一条线,然后从尾到下一点画了一条线。 begin_path
和 move_to
有什么用?
#[wasm_bindgen]
pub fn greet() -> Result<(), JsValue> {
utils::set_panic_hook();
let document = web_sys::window().unwrap().document().unwrap();
let canvas = document
.create_element("canvas")?
.dyn_into::<web_sys::HtmlCanvasElement>()?;
document.body().unwrap().append_child(&canvas)?;
canvas.set_width(640);
canvas.set_height(480);
canvas.style().set_property("border", "solid")?;
let context = canvas
.get_context("2d")?
.unwrap()
.dyn_into::<web_sys::CanvasRenderingContext2d>()?;
let context = Rc::new(context);
let pressed = Rc::new(Cell::new(false));
{
let context = context.clone();
let pressed = pressed.clone();
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
context.begin_path();
context.move_to(event.offset_x() as f64, event.offset_y() as f64);
pressed.set(true);
}) as Box<dyn FnMut(_)>);
canvas.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())?;
closure.forget();
}
{
let context = context.clone();
let pressed = pressed.clone();
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
if pressed.get() {
context.line_to(event.offset_x() as f64, event.offset_y() as f64);
context.stroke();
context.begin_path();
context.move_to(event.offset_x() as f64, event.offset_y() as f64);
}
}) as Box<dyn FnMut(_)>);
canvas.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())?;
closure.forget();
}
{
let context = context.clone();
let pressed = pressed.clone();
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
pressed.set(false);
context.line_to(event.offset_x() as f64, event.offset_y() as f64);
context.stroke();
}) as Box<dyn FnMut(_)>);
canvas.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())?;
closure.forget();
}
Ok(())
}
它是为了每次鼠标移动画一条线。
未能调用 beginPath()
,上下文的子路径仍将包含所有先前的绘制调用。因此,下次调用 stroke()
时,所有这些子路径都将被绘制,再次 ,覆盖之前的像素,从而产生丑陋的伪像。
然后调用moveTo
只是将未来的小段初始化为下一个鼠标移动向量。
这是一个示例,其中 strokeStyle
每次新的鼠标移动都会更改。由于我们不调用 beginPath()
,整个路径的颜色都发生了变化,而不仅仅是最后一段:
const canvas = document.querySelector( "canvas" );
const ctx = canvas.getContext("2d");
var hue = 0;
canvas.onmousemove = (evt) => {
const rect = canvas.getBoundingClientRect();
draw(evt.clientX - rect.left, evt.clientY - rect.top);
};
function draw(x, y) {
ctx.lineTo( x, y );
hue += 5;
ctx.strokeStyle = `hsl(${hue}deg,100%,50%)`;
ctx.stroke();
}
canvas {
border: 1px solid;
}
<canvas></canvas>
相比之下,调用 beginPath
时会发生以下情况:
const canvas = document.querySelector( "canvas" );
const ctx = canvas.getContext("2d");
var hue = 0;
canvas.onmousemove = (evt) => {
const rect = canvas.getBoundingClientRect();
draw(evt.clientX - rect.left, evt.clientY - rect.top);
};
function draw(x, y) {
ctx.lineTo( x, y );
hue += 5;
ctx.strokeStyle = `hsl(${hue}deg,100%,50%)`;
ctx.stroke();
ctx.beginPath();
ctx.moveTo( x, y );
}
canvas {
border: 1px solid;
}
<canvas></canvas>
但是,这可能会在线路交汇处产生问题,因为实际上没有接头。
因此,更好的方法是将所有向量存储在一个数组中,每帧清除整个上下文,并重绘完整路径:(抱歉,仍然在 JS 中,我的生锈是 rusted 不存在).
const canvas = document.querySelector( "canvas" );
const ctx = canvas.getContext("2d");
const vectors = [];
canvas.onmousemove = (evt) => {
const rect = canvas.getBoundingClientRect();
draw(evt.clientX - rect.left, evt.clientY - rect.top);
};
function draw(x, y) {
vectors.push( { x, y } );
ctx.clearRect( 0, 0, canvas.width, canvas.height );
ctx.beginPath();
vectors.forEach( ({x, y}) => ctx.lineTo( x, y ) );
ctx.stroke();
}
canvas {
border: 1px solid;
}
<canvas></canvas>