1use crate::*;
2
3impl Camera2D {
5 pub fn create(viewport_width: f64, viewport_height: f64) -> Camera2D {
16 Camera2D::new(
17 Vector2D::zero(),
18 RENDERER_DEFAULT_CAMERA_ZOOM,
19 RENDERER_DEFAULT_CAMERA_ROTATION,
20 viewport_width,
21 viewport_height,
22 )
23 }
24
25 pub fn world_to_screen(&self, world: Vector2D) -> Vector2D {
35 let relative: Vector2D = world - self.get_position();
36 let rotated: Vector2D = relative.rotated(-self.get_rotation());
37 Vector2D::new(
38 rotated.get_x() * self.get_zoom() + self.get_viewport_width() * 0.5,
39 rotated.get_y() * self.get_zoom() + self.get_viewport_height() * 0.5,
40 )
41 }
42
43 pub fn screen_to_world(&self, screen: Vector2D) -> Vector2D {
53 let relative: Vector2D = Vector2D::new(
54 (screen.get_x() - self.get_viewport_width() * 0.5) / self.get_zoom(),
55 (screen.get_y() - self.get_viewport_height() * 0.5) / self.get_zoom(),
56 );
57 let rotated: Vector2D = relative.rotated(self.get_rotation());
58 rotated + self.get_position()
59 }
60
61 pub fn translate(&mut self, offset: Vector2D) {
67 self.set_position(self.get_position() + offset);
68 }
69
70 pub fn zoom_by(&mut self, factor: f64) {
76 self.set_zoom((self.get_zoom() * factor).max(EPSILON));
77 }
78}
79
80impl Default for Camera2D {
82 fn default() -> Camera2D {
83 Camera2D::create(800.0, 600.0)
84 }
85}
86
87impl CanvasRenderer {
89 pub fn font<F>(size: f64, family: F) -> String
100 where
101 F: AsRef<str>,
102 {
103 let family: &str = family.as_ref();
104 format!("{size}px {family}")
105 }
106
107 pub fn default_font() -> String {
113 Self::font(RENDERER_DEFAULT_FONT_SIZE, RENDERER_DEFAULT_FONT_FAMILY)
114 }
115
116 pub fn enable_smoothing_on(context: &CanvasRenderingContext2d) {
130 Self::apply_quality(context, RenderQuality::High);
131 }
132
133 pub fn detect_dpr() -> f64 {
145 let window_value: Window = window().expect("no global window exists");
146 let raw: Option<f64> = Reflect::get(
147 window_value.as_ref(),
148 &JsValue::from_str(RENDERER_PROPERTY_DEVICE_PIXEL_RATIO),
149 )
150 .ok()
151 .and_then(|value: JsValue| value.as_f64());
152 raw.filter(|value: &f64| value.is_finite() && *value >= 1.0)
153 .unwrap_or(RENDERER_DEFAULT_DEVICE_PIXEL_RATIO)
154 }
155
156 pub(crate) fn apply_quality(context: &CanvasRenderingContext2d, quality: RenderQuality) {
168 let smoothing_enabled: bool = !matches!(quality, RenderQuality::Low);
169 let _ = Reflect::set(
170 context,
171 &JsValue::from_str(RENDERER_PROPERTY_IMAGE_SMOOTHING_ENABLED),
172 &JsValue::from_bool(smoothing_enabled),
173 );
174 let quality_value: &str = match quality {
175 RenderQuality::Low => RENDERER_IMAGE_SMOOTHING_QUALITY_LOW,
176 RenderQuality::Medium => RENDERER_IMAGE_SMOOTHING_QUALITY_MEDIUM,
177 RenderQuality::High => RENDERER_IMAGE_SMOOTHING_QUALITY_HIGH,
178 };
179 let _ = Reflect::set(
180 context,
181 &JsValue::from_str(RENDERER_PROPERTY_IMAGE_SMOOTHING_QUALITY),
182 &JsValue::from_str(quality_value),
183 );
184 let _ = Reflect::set(
185 context,
186 &JsValue::from_str(RENDERER_PROPERTY_TEXT_RENDERING),
187 &JsValue::from_str(RENDERER_TEXT_RENDERING_GEOMETRIC_PRECISION),
188 );
189 }
190}
191
192impl Color {
194 pub fn to_css(color: &Color) -> String {
204 color.to_css_rgba()
205 }
206}
207
208impl CanvasRenderer {
210 pub fn from_selector<S>(
222 canvas_selector: S,
223 viewport_width: f64,
224 viewport_height: f64,
225 ) -> Option<CanvasRenderer>
226 where
227 S: AsRef<str>,
228 {
229 let window_value: Window = window().expect("no global window exists");
230 let document_value: Document = window_value.document().expect("should have a document");
231 let element: Element = document_value
232 .query_selector(canvas_selector.as_ref())
233 .ok()
234 .flatten()?;
235 let canvas_element: HtmlCanvasElement = element.unchecked_into();
236 let context_object: Object = canvas_element
237 .get_context(RENDERER_CONTEXT_TYPE_2D)
238 .ok()
239 .flatten()?;
240 let context: CanvasRenderingContext2d = context_object.unchecked_into();
241 let renderer: CanvasRenderer = CanvasRenderer::new(
242 context,
243 Camera2D::create(viewport_width, viewport_height),
244 RenderQuality::default(),
245 );
246 renderer.enable_smoothing();
247 Some(renderer)
248 }
249
250 pub fn enable_smoothing(&self) {
256 Self::apply_quality(self.get_context(), self.get_quality());
257 }
258
259 pub fn clear(&self) {
261 self.get_context().clear_rect(
262 0.0,
263 0.0,
264 self.get_camera().get_viewport_width(),
265 self.get_camera().get_viewport_height(),
266 );
267 }
268
269 pub fn clear_color<C>(&self, color: C)
275 where
276 C: AsRef<str>,
277 {
278 let _ = Reflect::set(
279 self.get_context(),
280 &JsValue::from_str(RENDERER_PROPERTY_FILL_STYLE),
281 &JsValue::from_str(color.as_ref()),
282 );
283 self.get_context().fill_rect(
284 0.0,
285 0.0,
286 self.get_camera().get_viewport_width(),
287 self.get_camera().get_viewport_height(),
288 );
289 }
290
291 pub fn save(&self) {
293 self.get_context().save();
294 }
295
296 pub fn restore(&self) {
298 self.get_context().restore();
299 }
300
301 pub fn apply_camera(&self) {
306 let camera: Camera2D = self.get_camera();
307 let _ = self.get_context().translate(
308 camera.get_viewport_width() * 0.5,
309 camera.get_viewport_height() * 0.5,
310 );
311 let _ = self
312 .get_context()
313 .scale(camera.get_zoom(), camera.get_zoom());
314 let _ = self.get_context().rotate(camera.get_rotation());
315 let _ = self.get_context().translate(
316 -camera.get_position().get_x(),
317 -camera.get_position().get_y(),
318 );
319 }
320
321 pub fn set_fill_color<C>(&self, color: C)
327 where
328 C: AsRef<str>,
329 {
330 let _ = Reflect::set(
331 self.get_context(),
332 &JsValue::from_str(RENDERER_PROPERTY_FILL_STYLE),
333 &JsValue::from_str(color.as_ref()),
334 );
335 }
336
337 pub fn set_stroke_color<C>(&self, color: C)
343 where
344 C: AsRef<str>,
345 {
346 let _ = Reflect::set(
347 self.get_context(),
348 &JsValue::from_str(RENDERER_PROPERTY_STROKE_STYLE),
349 &JsValue::from_str(color.as_ref()),
350 );
351 }
352
353 pub fn set_line_width(&self, width: f64) {
359 let _ = Reflect::set(
360 self.get_context(),
361 &JsValue::from_str(RENDERER_PROPERTY_LINE_WIDTH),
362 &JsValue::from_f64(width),
363 );
364 }
365
366 pub fn set_global_alpha(&self, alpha: f64) {
372 let _ = Reflect::set(
373 self.get_context(),
374 &JsValue::from_str(RENDERER_PROPERTY_GLOBAL_ALPHA),
375 &JsValue::from_f64(Numeric::clamp(alpha, 0.0, 1.0)),
376 );
377 }
378
379 pub fn fill_rect(&self, position: Vector2D, width: f64, height: f64) {
387 self.get_context()
388 .fill_rect(position.get_x(), position.get_y(), width, height);
389 }
390
391 pub fn stroke_rect(&self, position: Vector2D, width: f64, height: f64) {
399 self.get_context()
400 .stroke_rect(position.get_x(), position.get_y(), width, height);
401 }
402
403 pub fn fill_circle(&self, center: Vector2D, radius: f64) {
410 self.get_context().begin_path();
411 self.get_context()
412 .arc(center.get_x(), center.get_y(), radius, 0.0, TWO_PI)
413 .unwrap_or(());
414 self.get_context().fill();
415 }
416
417 pub fn stroke_circle(&self, center: Vector2D, radius: f64) {
424 self.get_context().begin_path();
425 self.get_context()
426 .arc(center.get_x(), center.get_y(), radius, 0.0, TWO_PI)
427 .unwrap_or(());
428 self.get_context().stroke();
429 }
430
431 pub fn draw_line(&self, start: Vector2D, end: Vector2D) {
438 self.get_context().begin_path();
439 self.get_context().move_to(start.get_x(), start.get_y());
440 self.get_context().line_to(end.get_x(), end.get_y());
441 self.get_context().stroke();
442 }
443
444 pub fn fill_text<T>(&self, text: T, position: Vector2D)
451 where
452 T: AsRef<str>,
453 {
454 self.get_context()
455 .fill_text(text.as_ref(), position.get_x(), position.get_y())
456 .unwrap_or(());
457 }
458
459 pub fn set_font<F>(&self, font: F)
465 where
466 F: AsRef<str>,
467 {
468 let _ = Reflect::set(
469 self.get_context(),
470 &JsValue::from_str(RENDERER_PROPERTY_FONT),
471 &JsValue::from_str(font.as_ref()),
472 );
473 }
474
475 pub fn draw_image(
484 &self,
485 image: &HtmlImageElement,
486 position: Vector2D,
487 width: f64,
488 height: f64,
489 ) {
490 let _ = self
491 .get_context()
492 .draw_image_with_html_image_element_and_dw_and_dh(
493 image,
494 position.get_x(),
495 position.get_y(),
496 width,
497 height,
498 );
499 }
500
501 pub fn draw_image_rect(
511 &self,
512 image: &HtmlImageElement,
513 source: Rect,
514 dest_position: Vector2D,
515 dest_width: f64,
516 dest_height: f64,
517 ) {
518 let _ = self
519 .get_context()
520 .draw_image_with_html_image_element_and_sw_and_sh_and_dx_and_dy_and_dw_and_dh(
521 image,
522 source.get_x(),
523 source.get_y(),
524 source.get_width(),
525 source.get_height(),
526 dest_position.get_x(),
527 dest_position.get_y(),
528 dest_width,
529 dest_height,
530 );
531 }
532}
533
534impl Camera3D {
536 pub fn create(
549 position: Vector3D,
550 target: Vector3D,
551 viewport_width: f64,
552 viewport_height: f64,
553 ) -> Camera3D {
554 let mut camera: Camera3D = Camera3D::new(position, target, viewport_width, viewport_height);
555 camera.set_up(Vector3D::up());
556 camera.set_fov(DEFAULT_CAMERA_FOV);
557 camera.set_near(DEFAULT_CAMERA_NEAR);
558 camera.set_far(DEFAULT_CAMERA_FAR);
559 camera
560 }
561
562 pub fn aspect(&self) -> f64 {
568 if self.get_viewport_height() < EPSILON {
569 return 1.0;
570 }
571 self.get_viewport_width() / self.get_viewport_height()
572 }
573
574 pub fn forward(&self) -> Vector3D {
580 (self.get_target() - self.get_position()).normalized()
581 }
582
583 pub fn right(&self) -> Vector3D {
589 self.forward().cross(self.get_up()).normalized()
590 }
591
592 pub fn view_matrix(&self) -> Matrix4x4 {
598 Matrix4x4::look_at(self.get_position(), self.get_target(), self.get_up())
599 }
600
601 pub fn projection_matrix(&self) -> Matrix4x4 {
607 Matrix4x4::perspective(
608 self.get_fov(),
609 self.aspect(),
610 self.get_near(),
611 self.get_far(),
612 )
613 }
614
615 pub fn view_proj_matrix(&self) -> Matrix4x4 {
621 self.projection_matrix().multiply(self.view_matrix())
622 }
623
624 pub fn world_to_screen(&self, world: Vector3D) -> Vector3D {
634 let clip: Vector3D = self.view_proj_matrix().transform_point(world);
635 Vector3D::new(
636 (clip.get_x() + 1.0) * 0.5 * self.get_viewport_width(),
637 (1.0 - clip.get_y()) * 0.5 * self.get_viewport_height(),
638 clip.get_z(),
639 )
640 }
641
642 pub fn in_frustum(&self, world: Vector3D) -> bool {
652 let clip: Vector3D = self.view_proj_matrix().transform_point(world);
653 clip.get_x() >= -1.0
654 && clip.get_x() <= 1.0
655 && clip.get_y() >= -1.0
656 && clip.get_y() <= 1.0
657 && clip.get_z() >= -1.0
658 && clip.get_z() <= 1.0
659 }
660
661 pub fn translate(&mut self, offset: Vector3D) {
667 self.set_position(self.get_position() + offset);
668 self.set_target(self.get_target() + offset);
669 }
670
671 pub fn zoom(&mut self, distance: f64) {
677 let direction: Vector3D = self.forward();
678 self.set_position(self.get_position() + direction.scaled(distance));
679 }
680
681 pub fn orbit(&mut self, yaw_delta: f64, pitch_delta: f64) {
688 let offset: Vector3D = self.get_position() - self.get_target();
689 let current_distance: f64 = offset.magnitude();
690 let current_yaw: f64 = offset.get_x().atan2(offset.get_z());
691 let horizontal_dist: f64 =
692 (offset.get_x() * offset.get_x() + offset.get_z() * offset.get_z()).sqrt();
693 let current_pitch: f64 = (offset.get_y() / horizontal_dist.max(EPSILON)).asin();
694 let new_yaw: f64 = current_yaw + yaw_delta;
695 let new_pitch: f64 = Numeric::clamp(
696 current_pitch + pitch_delta,
697 -HALF_PI + EPSILON,
698 HALF_PI - EPSILON,
699 );
700 let cos_pitch: f64 = new_pitch.cos();
701 self.set_position(
702 self.get_target()
703 + Vector3D::new(
704 new_yaw.sin() * cos_pitch * current_distance,
705 new_pitch.sin() * current_distance,
706 new_yaw.cos() * cos_pitch * current_distance,
707 ),
708 );
709 }
710}
711
712impl Default for Camera3D {
714 fn default() -> Camera3D {
715 Camera3D::create(Vector3D::new(0.0, 0.0, 5.0), Vector3D::zero(), 800.0, 600.0)
716 }
717}
718
719impl SsaaCanvas {
721 pub fn from_selector<S>(canvas_selector: S, width: f64, height: f64) -> Option<SsaaCanvas>
733 where
734 S: AsRef<str>,
735 {
736 Self::from_selector_with_scale(
737 canvas_selector,
738 width,
739 height,
740 RENDERER_DEFAULT_SSAA_SCALE_FACTOR,
741 )
742 }
743
744 pub fn from_selector_with_scale<S>(
760 canvas_selector: S,
761 width: f64,
762 height: f64,
763 scale_factor: f64,
764 ) -> Option<SsaaCanvas>
765 where
766 S: AsRef<str>,
767 {
768 let window_value: Window = window().expect("no global window exists");
769 let document_value: Document = window_value.document().expect("should have a document");
770 let element: Element = document_value
771 .query_selector(canvas_selector.as_ref())
772 .ok()
773 .flatten()?;
774 let display_canvas: HtmlCanvasElement = element.unchecked_into();
775 let device_pixel_ratio: f64 = CanvasRenderer::detect_dpr();
776 let physical_width: u32 = (width * device_pixel_ratio).round() as u32;
777 let physical_height: u32 = (height * device_pixel_ratio).round() as u32;
778 display_canvas.set_width(physical_width);
779 display_canvas.set_height(physical_height);
780 let display_context_object: Object = display_canvas
781 .get_context(RENDERER_CONTEXT_TYPE_2D)
782 .ok()
783 .flatten()?;
784 let display_context: CanvasRenderingContext2d = display_context_object.unchecked_into();
785 let _ = display_context.scale(device_pixel_ratio, device_pixel_ratio);
786 let offscreen_canvas: HtmlCanvasElement = document_value
787 .create_element(RENDERER_ELEMENT_CANVAS)
788 .ok()?
789 .unchecked_into();
790 let scaled_width: u32 = (width * scale_factor * device_pixel_ratio).round() as u32;
791 let scaled_height: u32 = (height * scale_factor * device_pixel_ratio).round() as u32;
792 offscreen_canvas.set_width(scaled_width);
793 offscreen_canvas.set_height(scaled_height);
794 let offscreen_context_object: Object = offscreen_canvas
795 .get_context(RENDERER_CONTEXT_TYPE_2D)
796 .ok()
797 .flatten()?;
798 let offscreen_context: CanvasRenderingContext2d = offscreen_context_object.unchecked_into();
799 let _ = offscreen_context.scale(
800 scale_factor * device_pixel_ratio,
801 scale_factor * device_pixel_ratio,
802 );
803 let ssaa_canvas: SsaaCanvas = SsaaCanvas::new(
804 display_canvas,
805 display_context,
806 offscreen_canvas,
807 offscreen_context,
808 scale_factor,
809 width,
810 height,
811 );
812 ssaa_canvas.enable_smoothing();
813 Some(ssaa_canvas)
814 }
815
816 pub fn present(&self) {
823 CanvasRenderer::apply_quality(&self.display_context, self.get_quality());
824 self.display_context
825 .clear_rect(0.0, 0.0, self.width, self.height);
826 let _ = self
827 .display_context
828 .draw_image_with_html_canvas_element_and_dw_and_dh(
829 &self.offscreen_canvas,
830 0.0,
831 0.0,
832 self.width,
833 self.height,
834 );
835 }
836
837 pub fn clear(&self) {
839 self.offscreen_context
840 .clear_rect(0.0, 0.0, self.width, self.height);
841 }
842
843 pub fn clear_color<C>(&self, color: C)
849 where
850 C: AsRef<str>,
851 {
852 let _ = Reflect::set(
853 &self.offscreen_context,
854 &JsValue::from_str(RENDERER_PROPERTY_FILL_STYLE),
855 &JsValue::from_str(color.as_ref()),
856 );
857 self.offscreen_context
858 .fill_rect(0.0, 0.0, self.width, self.height);
859 }
860
861 pub fn enable_smoothing(&self) {
866 let quality: RenderQuality = self.get_quality();
867 CanvasRenderer::apply_quality(&self.display_context, quality);
868 CanvasRenderer::apply_quality(&self.offscreen_context, quality);
869 }
870}
871
872impl BlendMode {
874 pub fn to_css(&self) -> &str {
880 match self {
881 BlendMode::Normal => BLEND_MODE_NORMAL,
882 BlendMode::Multiply => BLEND_MODE_MULTIPLY,
883 BlendMode::Screen => BLEND_MODE_SCREEN,
884 BlendMode::Lighter => BLEND_MODE_LIGHTER,
885 BlendMode::Overlay => BLEND_MODE_OVERLAY,
886 BlendMode::Darken => BLEND_MODE_DARKEN,
887 BlendMode::Lighten => BLEND_MODE_LIGHTEN,
888 BlendMode::ColorDodge => BLEND_MODE_COLOR_DODGE,
889 BlendMode::ColorBurn => BLEND_MODE_COLOR_BURN,
890 BlendMode::HardLight => BLEND_MODE_HARD_LIGHT,
891 BlendMode::SoftLight => BLEND_MODE_SOFT_LIGHT,
892 BlendMode::Difference => BLEND_MODE_DIFFERENCE,
893 BlendMode::Exclusion => BLEND_MODE_EXCLUSION,
894 BlendMode::Hue => BLEND_MODE_HUE,
895 BlendMode::Saturation => BLEND_MODE_SATURATION,
896 BlendMode::Color => BLEND_MODE_COLOR,
897 BlendMode::Luminosity => BLEND_MODE_LUMINOSITY,
898 }
899 }
900}
901
902impl LinearGradient {
904 pub fn create(start: Vector2D, end: Vector2D, stops: Vec<(f64, String)>) -> LinearGradient {
916 LinearGradient::new(start, end, stops)
917 }
918
919 pub fn to_gradient(&self, context: &CanvasRenderingContext2d) -> Option<CanvasGradient> {
929 let canvas_gradient: CanvasGradient = context.create_linear_gradient(
930 self.get_start().get_x(),
931 self.get_start().get_y(),
932 self.get_end().get_x(),
933 self.get_end().get_y(),
934 );
935 for (position, color) in &self.stops {
936 let _ = canvas_gradient.add_color_stop(*position as f32, color);
937 }
938 Some(canvas_gradient)
939 }
940}
941
942impl RadialGradient {
944 pub fn create(
958 inner_center: Vector2D,
959 inner_radius: f64,
960 outer_center: Vector2D,
961 outer_radius: f64,
962 stops: Vec<(f64, String)>,
963 ) -> RadialGradient {
964 RadialGradient::new(
965 inner_center,
966 inner_radius,
967 outer_center,
968 outer_radius,
969 stops,
970 )
971 }
972
973 pub fn to_gradient(&self, context: &CanvasRenderingContext2d) -> Option<CanvasGradient> {
983 let canvas_gradient: CanvasGradient = context
984 .create_radial_gradient(
985 self.get_inner_center().get_x(),
986 self.get_inner_center().get_y(),
987 self.get_inner_radius(),
988 self.get_outer_center().get_x(),
989 self.get_outer_center().get_y(),
990 self.get_outer_radius(),
991 )
992 .ok()?;
993 for (position, color) in &self.stops {
994 let _ = canvas_gradient.add_color_stop(*position as f32, color);
995 }
996 Some(canvas_gradient)
997 }
998}
999
1000impl ShadowConfig {
1002 pub fn create() -> ShadowConfig {
1008 ShadowConfig::new(
1009 RENDERER_DEFAULT_SHADOW_COLOR.to_string(),
1010 RENDERER_DEFAULT_SHADOW_BLUR,
1011 0.0,
1012 0.0,
1013 )
1014 }
1015}
1016
1017impl Default for ShadowConfig {
1019 fn default() -> ShadowConfig {
1020 ShadowConfig::create()
1021 }
1022}
1023
1024impl RenderLayer {
1026 pub fn create(z_index: i32, visible: bool) -> RenderLayer {
1037 RenderLayer::new(z_index, visible)
1038 }
1039
1040 pub fn background() -> RenderLayer {
1046 RenderLayer::new(RENDERER_LAYER_BACKGROUND, true)
1047 }
1048
1049 pub fn foreground() -> RenderLayer {
1055 RenderLayer::new(RENDERER_LAYER_FOREGROUND, true)
1056 }
1057
1058 pub fn ui() -> RenderLayer {
1064 RenderLayer::new(RENDERER_LAYER_UI, true)
1065 }
1066}
1067
1068impl CanvasRenderer {
1070 pub fn set_blend_mode(&self, mode: BlendMode) {
1076 let _ = Reflect::set(
1077 self.get_context(),
1078 &JsValue::from_str(RENDERER_PROPERTY_GLOBAL_COMPOSITE_OPERATION),
1079 &JsValue::from_str(mode.to_css()),
1080 );
1081 }
1082
1083 pub fn set_shadow(&self, config: &ShadowConfig) {
1089 let _ = Reflect::set(
1090 self.get_context(),
1091 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_COLOR),
1092 &JsValue::from_str(config.get_color().as_str()),
1093 );
1094 let _ = Reflect::set(
1095 self.get_context(),
1096 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_BLUR),
1097 &JsValue::from_f64(config.get_blur()),
1098 );
1099 let _ = Reflect::set(
1100 self.get_context(),
1101 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_OFFSET_X),
1102 &JsValue::from_f64(config.get_offset_x()),
1103 );
1104 let _ = Reflect::set(
1105 self.get_context(),
1106 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_OFFSET_Y),
1107 &JsValue::from_f64(config.get_offset_y()),
1108 );
1109 }
1110
1111 pub fn clear_shadow(&self) {
1113 let _ = Reflect::set(
1114 self.get_context(),
1115 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_COLOR),
1116 &JsValue::from_str("rgba(0, 0, 0, 0)"),
1117 );
1118 let _ = Reflect::set(
1119 self.get_context(),
1120 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_BLUR),
1121 &JsValue::from_f64(0.0),
1122 );
1123 let _ = Reflect::set(
1124 self.get_context(),
1125 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_OFFSET_X),
1126 &JsValue::from_f64(0.0),
1127 );
1128 let _ = Reflect::set(
1129 self.get_context(),
1130 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_OFFSET_Y),
1131 &JsValue::from_f64(0.0),
1132 );
1133 }
1134
1135 pub fn set_linear_gradient_fill(&self, gradient: &LinearGradient) {
1141 if let Some(canvas_gradient) = gradient.to_gradient(self.get_context()) {
1142 let _ = Reflect::set(
1143 self.get_context(),
1144 &JsValue::from_str(RENDERER_PROPERTY_FILL_STYLE),
1145 &canvas_gradient,
1146 );
1147 }
1148 }
1149
1150 pub fn set_radial_gradient_fill(&self, gradient: &RadialGradient) {
1156 if let Some(canvas_gradient) = gradient.to_gradient(self.get_context()) {
1157 let _ = Reflect::set(
1158 self.get_context(),
1159 &JsValue::from_str(RENDERER_PROPERTY_FILL_STYLE),
1160 &canvas_gradient,
1161 );
1162 }
1163 }
1164
1165 pub fn set_linear_gradient_stroke(&self, gradient: &LinearGradient) {
1171 if let Some(canvas_gradient) = gradient.to_gradient(self.get_context()) {
1172 let _ = Reflect::set(
1173 self.get_context(),
1174 &JsValue::from_str(RENDERER_PROPERTY_STROKE_STYLE),
1175 &canvas_gradient,
1176 );
1177 }
1178 }
1179
1180 pub fn set_radial_gradient_stroke(&self, gradient: &RadialGradient) {
1186 if let Some(canvas_gradient) = gradient.to_gradient(self.get_context()) {
1187 let _ = Reflect::set(
1188 self.get_context(),
1189 &JsValue::from_str(RENDERER_PROPERTY_STROKE_STYLE),
1190 &canvas_gradient,
1191 );
1192 }
1193 }
1194}
1195
1196impl RenderBackend for CanvasRenderer {
1199 fn clear(&self) {
1200 self.clear();
1201 }
1202
1203 fn clear_color<C>(&self, color: C)
1204 where
1205 C: AsRef<str>,
1206 {
1207 self.clear_color(color);
1208 }
1209
1210 fn save(&self) {
1211 self.save();
1212 }
1213
1214 fn restore(&self) {
1215 self.restore();
1216 }
1217
1218 fn set_fill_color(&self, color: &str) {
1219 self.set_fill_color(color);
1220 }
1221
1222 fn set_stroke_color(&self, color: &str) {
1223 self.set_stroke_color(color);
1224 }
1225
1226 fn set_line_width(&self, width: f64) {
1227 self.set_line_width(width);
1228 }
1229
1230 fn set_global_alpha(&self, alpha: f64) {
1231 self.set_global_alpha(alpha);
1232 }
1233
1234 fn set_blend_mode(&self, mode: BlendMode) {
1235 self.set_blend_mode(mode);
1236 }
1237
1238 fn set_shadow(&self, config: &ShadowConfig) {
1239 self.set_shadow(config);
1240 }
1241
1242 fn clear_shadow(&self) {
1243 self.clear_shadow();
1244 }
1245
1246 fn fill_rect(&self, position: Vector2D, width: f64, height: f64) {
1247 self.fill_rect(position, width, height);
1248 }
1249
1250 fn stroke_rect(&self, position: Vector2D, width: f64, height: f64) {
1251 self.stroke_rect(position, width, height);
1252 }
1253
1254 fn fill_circle(&self, center: Vector2D, radius: f64) {
1255 self.fill_circle(center, radius);
1256 }
1257
1258 fn stroke_circle(&self, center: Vector2D, radius: f64) {
1259 self.stroke_circle(center, radius);
1260 }
1261
1262 fn draw_line(&self, start: Vector2D, end: Vector2D) {
1263 self.draw_line(start, end);
1264 }
1265
1266 fn fill_text(&self, text: &str, position: Vector2D) {
1267 self.fill_text(text, position);
1268 }
1269
1270 fn set_font(&self, font: &str) {
1271 self.set_font(font);
1272 }
1273
1274 fn draw_image(&self, image: &HtmlImageElement, position: Vector2D, width: f64, height: f64) {
1275 self.draw_image(image, position, width, height);
1276 }
1277
1278 fn set_linear_gradient_fill(&self, gradient: &LinearGradient) {
1279 self.set_linear_gradient_fill(gradient);
1280 }
1281
1282 fn set_radial_gradient_fill(&self, gradient: &RadialGradient) {
1283 self.set_radial_gradient_fill(gradient);
1284 }
1285}