Yew框架MNIST ONNX绘图组件Rust所有权问题求助
Rust Yew框架MNIST绘图组件所有权问题修复方案
问题核心
在Yew框架复现MNIST ONNX Runtime绘图组件时,draw回调中的所有权处理逻辑错误:通过clone创建临时向量导致后续循环无法正确访问笔画数据,且循环内错误将points覆盖为第一个笔画的克隆,完全偏离了绘制当前笔画的逻辑。
问题代码分析
原代码中存在两个关键问题:
let mut points = strokes.last().and_then(|p| Some(p.clone())).unwrap_or_else(|| Vec::new());每次创建新向量,未将新点同步回draw_context的状态中- 循环内
points = strokes[0].clone();错误覆盖了当前要绘制的点集合,导致遍历对象错误
原问题代码片段:
let draw = Callback::from(move |e: MouseEvent| { let canvas = canvas_node_ref.cast::<HtmlCanvasElement>(); log::info!("draw"); if let Some(canvas) = canvas { let ctx = match canvas.get_context("2d") { Ok(Some(ctx)) => ctx, _ => return, } .dyn_into::<web_sys::CanvasRenderingContext2d>() .unwrap(); ctx.set_line_width(20.0); ctx.set_line_join("round"); ctx.set_line_cap("round"); ctx.set_stroke_style(&JsValue::from_str("#393E46")); ctx.clear_rect(0.0, 0.0, canvas.width() as f64, canvas.height() as f64); let strokes = draw_context_clone.state().strokes; let mut points = strokes.last().and_then(|p| Some(p.clone())).unwrap_or_else(|| Vec::new()); points.push(math_utils::get_coordinates(e)); let len = points.len(); for s in 0..len-1 { points = strokes[0].clone(); let p1 = points[s]; let p2 = points[s + 1]; log::info!("inside for loopp {}", p1.0); ctx.begin_path(); ctx.move_to(p1.0 as f64, p1.1 as f64); let midpoint = math_utils::get_midpoint(p1, p2); ctx.quadratic_curve_to(p1.0 as f64, p1.1 as f64, midpoint.0 as f64, midpoint.1 as f64); ctx.stroke(); } } }); let draw_clone = draw.clone(); let activate_draw = Callback::from(move |e: MouseEvent| { log::info!("activate_draw"); drawing.set(!*drawing); draw_context.new_stroke(); let strokes = draw_context.state().strokes; let mut points = strokes.last().and_then(|p| Some(p.clone())).unwrap_or_else(|| Vec::new()); log::info!("{}", points.len()); points.push(math_utils::get_coordinates(e.clone())); draw_clone.emit(e); });
修复后的代码
let draw = Callback::from(move |e: MouseEvent| { // 仅在绘图激活状态下处理 if !*drawing.get() { return; } let canvas = canvas_node_ref.cast::<HtmlCanvasElement>(); log::info!("draw"); if let Some(canvas) = canvas { let ctx = match canvas.get_context("2d") { Ok(Some(ctx)) => ctx, _ => return, } .dyn_into::<web_sys::CanvasRenderingContext2d>() .unwrap(); ctx.set_line_width(20.0); ctx.set_line_join("round"); ctx.set_line_cap("round"); ctx.set_stroke_style(&JsValue::from_str("#393E46")); ctx.clear_rect(0.0, 0.0, canvas.width() as f64, canvas.height() as f64); // 获取当前状态的可变引用,直接更新笔画数据 let mut state = draw_context_clone.state_mut(); let current_stroke = state.strokes.last_mut().unwrap_or_else(|| { state.strokes.push(Vec::new()); state.strokes.last_mut().unwrap() }); current_stroke.push(math_utils::get_coordinates(e)); // 重新绘制所有笔画 for stroke in &state.strokes { if stroke.len() < 2 { continue; } ctx.begin_path(); let mut prev_point = stroke[0]; ctx.move_to(prev_point.0 as f64, prev_point.1 as f64); for i in 1..stroke.len() { let curr_point = stroke[i]; let midpoint = math_utils::get_midpoint(prev_point, curr_point); ctx.quadratic_curve_to(prev_point.0 as f64, prev_point.1 as f64, midpoint.0 as f64, midpoint.1 as f64); prev_point = curr_point; } ctx.stroke(); } } }); let draw_clone = draw.clone(); let activate_draw = Callback::from(move |e: MouseEvent| { log::info!("activate_draw"); let new_drawing_state = !*drawing.get(); drawing.set(new_drawing_state); if new_drawing_state { // 激活绘图时创建新笔画 let mut state = draw_context.state_mut(); state.strokes.push(Vec::new()); let current_stroke = state.strokes.last_mut().unwrap(); current_stroke.push(math_utils::get_coordinates(e.clone())); } draw_clone.emit(e); });
关键改进说明
- 避免不必要的Clone:通过
state_mut()获取draw_context的可变引用,直接修改内部的笔画向量,无需克隆整个向量 - 修正绘制逻辑:循环遍历所有笔画,对每个笔画内的点依次绘制二次贝塞尔曲线,确保所有已绘制内容都被正确渲染
- 状态同步:在
activate_draw中直接更新draw_context的状态,而不是创建临时向量后丢弃,保证UI状态与数据一致 - 添加状态判断:在
draw回调开头检查绘图激活状态,避免无效的鼠标移动事件处理
内容的提问来源于stack exchange,提问作者Pash
相关产品推荐
相关产品推荐

