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 build_font_string(size: f64, family: &str) -> String {
100 format!("{}px {}", size, family)
101 }
102
103 pub fn default_font_string() -> String {
109 Self::build_font_string(RENDERER_DEFAULT_FONT_SIZE, RENDERER_DEFAULT_FONT_FAMILY)
110 }
111
112 pub fn enable_context_anti_aliasing(context: &CanvasRenderingContext2d) {
122 let _ = Reflect::set(
123 context,
124 &JsValue::from_str(RENDERER_PROPERTY_IMAGE_SMOOTHING_ENABLED),
125 &JsValue::from_bool(true),
126 );
127 let _ = Reflect::set(
128 context,
129 &JsValue::from_str(RENDERER_PROPERTY_IMAGE_SMOOTHING_QUALITY),
130 &JsValue::from_str(RENDERER_IMAGE_SMOOTHING_QUALITY_HIGH),
131 );
132 }
133}
134
135impl Color {
137 pub fn to_css(color: &Color) -> String {
147 color.to_css_rgba()
148 }
149}
150
151impl CanvasRenderer {
153 pub fn from_selector(
165 canvas_selector: &str,
166 viewport_width: f64,
167 viewport_height: f64,
168 ) -> Option<CanvasRenderer> {
169 let window_value: Window = window().expect("no global window exists");
170 let document_value: Document = window_value.document().expect("should have a document");
171 let element: Element = document_value
172 .query_selector(canvas_selector)
173 .ok()
174 .flatten()?;
175 let canvas_element: HtmlCanvasElement = element.unchecked_into();
176 let context_object: Object = canvas_element
177 .get_context(RENDERER_CONTEXT_TYPE_2D)
178 .ok()
179 .flatten()?;
180 let context: CanvasRenderingContext2d = context_object.unchecked_into();
181 Some(CanvasRenderer::new(
182 context,
183 Camera2D::create(viewport_width, viewport_height),
184 ))
185 }
186
187 pub fn enable_anti_aliasing(&self) {
194 let _ = Reflect::set(
195 self.get_context(),
196 &JsValue::from_str(RENDERER_PROPERTY_IMAGE_SMOOTHING_ENABLED),
197 &JsValue::from_bool(true),
198 );
199 let _ = Reflect::set(
200 self.get_context(),
201 &JsValue::from_str(RENDERER_PROPERTY_IMAGE_SMOOTHING_QUALITY),
202 &JsValue::from_str(RENDERER_IMAGE_SMOOTHING_QUALITY_HIGH),
203 );
204 }
205
206 pub fn clear(&self) {
208 self.get_context().clear_rect(
209 0.0,
210 0.0,
211 self.get_camera().get_viewport_width(),
212 self.get_camera().get_viewport_height(),
213 );
214 }
215
216 pub fn clear_with_color(&self, color: &str) {
222 let _ = Reflect::set(
223 self.get_context(),
224 &JsValue::from_str(RENDERER_PROPERTY_FILL_STYLE),
225 &JsValue::from_str(color),
226 );
227 self.get_context().fill_rect(
228 0.0,
229 0.0,
230 self.get_camera().get_viewport_width(),
231 self.get_camera().get_viewport_height(),
232 );
233 }
234
235 pub fn save(&self) {
237 self.get_context().save();
238 }
239
240 pub fn restore(&self) {
242 self.get_context().restore();
243 }
244
245 pub fn apply_camera(&self) {
250 let camera: Camera2D = self.get_camera();
251 let _ = self.get_context().translate(
252 camera.get_viewport_width() * 0.5,
253 camera.get_viewport_height() * 0.5,
254 );
255 let _ = self
256 .get_context()
257 .scale(camera.get_zoom(), camera.get_zoom());
258 let _ = self.get_context().rotate(camera.get_rotation());
259 let _ = self.get_context().translate(
260 -camera.get_position().get_x(),
261 -camera.get_position().get_y(),
262 );
263 }
264
265 pub fn set_fill_color(&self, color: &str) {
271 let _ = Reflect::set(
272 self.get_context(),
273 &JsValue::from_str(RENDERER_PROPERTY_FILL_STYLE),
274 &JsValue::from_str(color),
275 );
276 }
277
278 pub fn set_stroke_color(&self, color: &str) {
284 let _ = Reflect::set(
285 self.get_context(),
286 &JsValue::from_str(RENDERER_PROPERTY_STROKE_STYLE),
287 &JsValue::from_str(color),
288 );
289 }
290
291 pub fn set_line_width(&self, width: f64) {
297 let _ = Reflect::set(
298 self.get_context(),
299 &JsValue::from_str(RENDERER_PROPERTY_LINE_WIDTH),
300 &JsValue::from_f64(width),
301 );
302 }
303
304 pub fn set_global_alpha(&self, alpha: f64) {
310 let _ = Reflect::set(
311 self.get_context(),
312 &JsValue::from_str(RENDERER_PROPERTY_GLOBAL_ALPHA),
313 &JsValue::from_f64(Numeric::clamp(alpha, 0.0, 1.0)),
314 );
315 }
316
317 pub fn fill_rect(&self, position: Vector2D, width: f64, height: f64) {
325 self.get_context()
326 .fill_rect(position.get_x(), position.get_y(), width, height);
327 }
328
329 pub fn stroke_rect(&self, position: Vector2D, width: f64, height: f64) {
337 self.get_context()
338 .stroke_rect(position.get_x(), position.get_y(), width, height);
339 }
340
341 pub fn fill_circle(&self, center: Vector2D, radius: f64) {
348 self.get_context().begin_path();
349 self.get_context()
350 .arc(center.get_x(), center.get_y(), radius, 0.0, TWO_PI)
351 .unwrap_or(());
352 self.get_context().fill();
353 }
354
355 pub fn stroke_circle(&self, center: Vector2D, radius: f64) {
362 self.get_context().begin_path();
363 self.get_context()
364 .arc(center.get_x(), center.get_y(), radius, 0.0, TWO_PI)
365 .unwrap_or(());
366 self.get_context().stroke();
367 }
368
369 pub fn draw_line(&self, start: Vector2D, end: Vector2D) {
376 self.get_context().begin_path();
377 self.get_context().move_to(start.get_x(), start.get_y());
378 self.get_context().line_to(end.get_x(), end.get_y());
379 self.get_context().stroke();
380 }
381
382 pub fn fill_text(&self, text: &str, position: Vector2D) {
389 self.get_context()
390 .fill_text(text, position.get_x(), position.get_y())
391 .unwrap_or(());
392 }
393
394 pub fn set_font(&self, font: &str) {
400 let _ = Reflect::set(
401 self.get_context(),
402 &JsValue::from_str(RENDERER_PROPERTY_FONT),
403 &JsValue::from_str(font),
404 );
405 }
406
407 pub fn draw_image(
416 &self,
417 image: &HtmlImageElement,
418 position: Vector2D,
419 width: f64,
420 height: f64,
421 ) {
422 let _ = self
423 .get_context()
424 .draw_image_with_html_image_element_and_dw_and_dh(
425 image,
426 position.get_x(),
427 position.get_y(),
428 width,
429 height,
430 );
431 }
432
433 pub fn draw_image_subregion(
443 &self,
444 image: &HtmlImageElement,
445 source: Rect,
446 dest_position: Vector2D,
447 dest_width: f64,
448 dest_height: f64,
449 ) {
450 let _ = self
451 .get_context()
452 .draw_image_with_html_image_element_and_sw_and_sh_and_dx_and_dy_and_dw_and_dh(
453 image,
454 source.get_x(),
455 source.get_y(),
456 source.get_width(),
457 source.get_height(),
458 dest_position.get_x(),
459 dest_position.get_y(),
460 dest_width,
461 dest_height,
462 );
463 }
464}
465
466impl Camera3D {
468 pub fn create(
481 position: Vector3D,
482 target: Vector3D,
483 viewport_width: f64,
484 viewport_height: f64,
485 ) -> Camera3D {
486 let mut camera: Camera3D = Camera3D::new(position, target, viewport_width, viewport_height);
487 camera.set_up(Vector3D::up());
488 camera.set_fov(DEFAULT_CAMERA_FOV);
489 camera.set_near(DEFAULT_CAMERA_NEAR);
490 camera.set_far(DEFAULT_CAMERA_FAR);
491 camera
492 }
493
494 pub fn aspect(&self) -> f64 {
500 if self.get_viewport_height() < EPSILON {
501 return 1.0;
502 }
503 self.get_viewport_width() / self.get_viewport_height()
504 }
505
506 pub fn forward(&self) -> Vector3D {
512 (self.get_target() - self.get_position()).normalized()
513 }
514
515 pub fn right(&self) -> Vector3D {
521 self.forward().cross(self.get_up()).normalized()
522 }
523
524 pub fn view_matrix(&self) -> Matrix4x4 {
530 Matrix4x4::look_at(self.get_position(), self.get_target(), self.get_up())
531 }
532
533 pub fn projection_matrix(&self) -> Matrix4x4 {
539 Matrix4x4::perspective(
540 self.get_fov(),
541 self.aspect(),
542 self.get_near(),
543 self.get_far(),
544 )
545 }
546
547 pub fn view_projection_matrix(&self) -> Matrix4x4 {
553 self.projection_matrix().multiply(self.view_matrix())
554 }
555
556 pub fn world_to_screen(&self, world: Vector3D) -> Vector3D {
566 let clip: Vector3D = self.view_projection_matrix().transform_point(world);
567 Vector3D::new(
568 (clip.get_x() + 1.0) * 0.5 * self.get_viewport_width(),
569 (1.0 - clip.get_y()) * 0.5 * self.get_viewport_height(),
570 clip.get_z(),
571 )
572 }
573
574 pub fn is_in_frustum(&self, world: Vector3D) -> bool {
584 let clip: Vector3D = self.view_projection_matrix().transform_point(world);
585 clip.get_x() >= -1.0
586 && clip.get_x() <= 1.0
587 && clip.get_y() >= -1.0
588 && clip.get_y() <= 1.0
589 && clip.get_z() >= -1.0
590 && clip.get_z() <= 1.0
591 }
592
593 pub fn translate(&mut self, offset: Vector3D) {
599 self.set_position(self.get_position() + offset);
600 self.set_target(self.get_target() + offset);
601 }
602
603 pub fn zoom(&mut self, distance: f64) {
609 let direction: Vector3D = self.forward();
610 self.set_position(self.get_position() + direction.scaled(distance));
611 }
612
613 pub fn orbit(&mut self, yaw_delta: f64, pitch_delta: f64) {
620 let offset: Vector3D = self.get_position() - self.get_target();
621 let current_distance: f64 = offset.magnitude();
622 let current_yaw: f64 = offset.get_x().atan2(offset.get_z());
623 let horizontal_dist: f64 =
624 (offset.get_x() * offset.get_x() + offset.get_z() * offset.get_z()).sqrt();
625 let current_pitch: f64 = (offset.get_y() / horizontal_dist.max(EPSILON)).asin();
626 let new_yaw: f64 = current_yaw + yaw_delta;
627 let new_pitch: f64 = Numeric::clamp(
628 current_pitch + pitch_delta,
629 -HALF_PI + EPSILON,
630 HALF_PI - EPSILON,
631 );
632 let cos_pitch: f64 = new_pitch.cos();
633 self.set_position(
634 self.get_target()
635 + Vector3D::new(
636 new_yaw.sin() * cos_pitch * current_distance,
637 new_pitch.sin() * current_distance,
638 new_yaw.cos() * cos_pitch * current_distance,
639 ),
640 );
641 }
642}
643
644impl Default for Camera3D {
646 fn default() -> Camera3D {
647 Camera3D::create(Vector3D::new(0.0, 0.0, 5.0), Vector3D::zero(), 800.0, 600.0)
648 }
649}
650
651impl SsaaCanvas {
653 pub fn from_selector(canvas_selector: &str, width: f64, height: f64) -> Option<SsaaCanvas> {
665 Self::from_selector_with_scale(
666 canvas_selector,
667 width,
668 height,
669 RENDERER_DEFAULT_SSAA_SCALE_FACTOR,
670 )
671 }
672
673 pub fn from_selector_with_scale(
689 canvas_selector: &str,
690 width: f64,
691 height: f64,
692 scale_factor: f64,
693 ) -> Option<SsaaCanvas> {
694 let window_value: Window = window().expect("no global window exists");
695 let document_value: Document = window_value.document().expect("should have a document");
696 let element: Element = document_value
697 .query_selector(canvas_selector)
698 .ok()
699 .flatten()?;
700 let display_canvas: HtmlCanvasElement = element.unchecked_into();
701 display_canvas.set_width(width as u32);
702 display_canvas.set_height(height as u32);
703 let display_context_object: Object = display_canvas
704 .get_context(RENDERER_CONTEXT_TYPE_2D)
705 .ok()
706 .flatten()?;
707 let display_context: CanvasRenderingContext2d = display_context_object.unchecked_into();
708 let offscreen_canvas: HtmlCanvasElement = document_value
709 .create_element(RENDERER_ELEMENT_CANVAS)
710 .ok()?
711 .unchecked_into();
712 let scaled_width: u32 = (width * scale_factor) as u32;
713 let scaled_height: u32 = (height * scale_factor) as u32;
714 offscreen_canvas.set_width(scaled_width);
715 offscreen_canvas.set_height(scaled_height);
716 let offscreen_context_object: Object = offscreen_canvas
717 .get_context(RENDERER_CONTEXT_TYPE_2D)
718 .ok()
719 .flatten()?;
720 let offscreen_context: CanvasRenderingContext2d = offscreen_context_object.unchecked_into();
721 let _ = offscreen_context.scale(scale_factor, scale_factor);
722 let ssaa_canvas: SsaaCanvas = SsaaCanvas::new(
723 display_canvas,
724 display_context,
725 offscreen_canvas,
726 offscreen_context,
727 scale_factor,
728 width,
729 height,
730 );
731 ssaa_canvas.enable_anti_aliasing();
732 Some(ssaa_canvas)
733 }
734
735 pub fn present(&self) {
742 let _ = Reflect::set(
743 &self.display_context,
744 &JsValue::from_str(RENDERER_PROPERTY_IMAGE_SMOOTHING_ENABLED),
745 &JsValue::from_bool(true),
746 );
747 let _ = Reflect::set(
748 &self.display_context,
749 &JsValue::from_str(RENDERER_PROPERTY_IMAGE_SMOOTHING_QUALITY),
750 &JsValue::from_str(RENDERER_IMAGE_SMOOTHING_QUALITY_HIGH),
751 );
752 self.display_context
753 .clear_rect(0.0, 0.0, self.width, self.height);
754 let _ = self
755 .display_context
756 .draw_image_with_html_canvas_element_and_dw_and_dh(
757 &self.offscreen_canvas,
758 0.0,
759 0.0,
760 self.width,
761 self.height,
762 );
763 }
764
765 pub fn clear(&self) {
767 self.offscreen_context
768 .clear_rect(0.0, 0.0, self.width, self.height);
769 }
770
771 pub fn clear_with_color(&self, color: &str) {
777 let _ = Reflect::set(
778 &self.offscreen_context,
779 &JsValue::from_str(RENDERER_PROPERTY_FILL_STYLE),
780 &JsValue::from_str(color),
781 );
782 self.offscreen_context
783 .fill_rect(0.0, 0.0, self.width, self.height);
784 }
785
786 pub fn enable_anti_aliasing(&self) {
791 let _ = Reflect::set(
792 &self.display_context,
793 &JsValue::from_str(RENDERER_PROPERTY_IMAGE_SMOOTHING_ENABLED),
794 &JsValue::from_bool(true),
795 );
796 let _ = Reflect::set(
797 &self.display_context,
798 &JsValue::from_str(RENDERER_PROPERTY_IMAGE_SMOOTHING_QUALITY),
799 &JsValue::from_str(RENDERER_IMAGE_SMOOTHING_QUALITY_HIGH),
800 );
801 let _ = Reflect::set(
802 &self.offscreen_context,
803 &JsValue::from_str(RENDERER_PROPERTY_IMAGE_SMOOTHING_ENABLED),
804 &JsValue::from_bool(true),
805 );
806 let _ = Reflect::set(
807 &self.offscreen_context,
808 &JsValue::from_str(RENDERER_PROPERTY_IMAGE_SMOOTHING_QUALITY),
809 &JsValue::from_str(RENDERER_IMAGE_SMOOTHING_QUALITY_HIGH),
810 );
811 }
812}
813
814impl BlendMode {
816 pub fn to_css_string(&self) -> &str {
822 match self {
823 BlendMode::Normal => BLEND_MODE_NORMAL,
824 BlendMode::Multiply => BLEND_MODE_MULTIPLY,
825 BlendMode::Screen => BLEND_MODE_SCREEN,
826 BlendMode::Lighter => BLEND_MODE_LIGHTER,
827 BlendMode::Overlay => BLEND_MODE_OVERLAY,
828 BlendMode::Darken => BLEND_MODE_DARKEN,
829 BlendMode::Lighten => BLEND_MODE_LIGHTEN,
830 BlendMode::ColorDodge => BLEND_MODE_COLOR_DODGE,
831 BlendMode::ColorBurn => BLEND_MODE_COLOR_BURN,
832 BlendMode::HardLight => BLEND_MODE_HARD_LIGHT,
833 BlendMode::SoftLight => BLEND_MODE_SOFT_LIGHT,
834 BlendMode::Difference => BLEND_MODE_DIFFERENCE,
835 BlendMode::Exclusion => BLEND_MODE_EXCLUSION,
836 BlendMode::Hue => BLEND_MODE_HUE,
837 BlendMode::Saturation => BLEND_MODE_SATURATION,
838 BlendMode::Color => BLEND_MODE_COLOR,
839 BlendMode::Luminosity => BLEND_MODE_LUMINOSITY,
840 }
841 }
842}
843
844impl LinearGradient {
846 pub fn create(start: Vector2D, end: Vector2D, stops: Vec<(f64, String)>) -> LinearGradient {
858 LinearGradient::new(start, end, stops)
859 }
860
861 pub fn to_canvas_gradient(&self, context: &CanvasRenderingContext2d) -> Option<CanvasGradient> {
871 let canvas_gradient: CanvasGradient = context.create_linear_gradient(
872 self.get_start().get_x(),
873 self.get_start().get_y(),
874 self.get_end().get_x(),
875 self.get_end().get_y(),
876 );
877 for (position, color) in &self.stops {
878 let _ = canvas_gradient.add_color_stop(*position as f32, color);
879 }
880 Some(canvas_gradient)
881 }
882}
883
884impl RadialGradient {
886 pub fn create(
900 inner_center: Vector2D,
901 inner_radius: f64,
902 outer_center: Vector2D,
903 outer_radius: f64,
904 stops: Vec<(f64, String)>,
905 ) -> RadialGradient {
906 RadialGradient::new(
907 inner_center,
908 inner_radius,
909 outer_center,
910 outer_radius,
911 stops,
912 )
913 }
914
915 pub fn to_canvas_gradient(&self, context: &CanvasRenderingContext2d) -> Option<CanvasGradient> {
925 let canvas_gradient: CanvasGradient = context
926 .create_radial_gradient(
927 self.get_inner_center().get_x(),
928 self.get_inner_center().get_y(),
929 self.get_inner_radius(),
930 self.get_outer_center().get_x(),
931 self.get_outer_center().get_y(),
932 self.get_outer_radius(),
933 )
934 .ok()?;
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 ShadowConfig {
944 pub fn create() -> ShadowConfig {
950 ShadowConfig::new(
951 RENDERER_DEFAULT_SHADOW_COLOR.to_string(),
952 RENDERER_DEFAULT_SHADOW_BLUR,
953 0.0,
954 0.0,
955 )
956 }
957}
958
959impl Default for ShadowConfig {
961 fn default() -> ShadowConfig {
962 ShadowConfig::create()
963 }
964}
965
966impl RenderLayer {
968 pub fn create(z_index: i32, visible: bool) -> RenderLayer {
979 RenderLayer::new(z_index, visible)
980 }
981
982 pub fn background() -> RenderLayer {
988 RenderLayer::new(RENDERER_LAYER_BACKGROUND, true)
989 }
990
991 pub fn foreground() -> RenderLayer {
997 RenderLayer::new(RENDERER_LAYER_FOREGROUND, true)
998 }
999
1000 pub fn ui() -> RenderLayer {
1006 RenderLayer::new(RENDERER_LAYER_UI, true)
1007 }
1008}
1009
1010impl CanvasRenderer {
1012 pub fn set_blend_mode(&self, mode: BlendMode) {
1018 let _ = Reflect::set(
1019 self.get_context(),
1020 &JsValue::from_str(RENDERER_PROPERTY_GLOBAL_COMPOSITE_OPERATION),
1021 &JsValue::from_str(mode.to_css_string()),
1022 );
1023 }
1024
1025 pub fn set_shadow(&self, config: &ShadowConfig) {
1031 let _ = Reflect::set(
1032 self.get_context(),
1033 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_COLOR),
1034 &JsValue::from_str(config.get_color().as_str()),
1035 );
1036 let _ = Reflect::set(
1037 self.get_context(),
1038 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_BLUR),
1039 &JsValue::from_f64(config.get_blur()),
1040 );
1041 let _ = Reflect::set(
1042 self.get_context(),
1043 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_OFFSET_X),
1044 &JsValue::from_f64(config.get_offset_x()),
1045 );
1046 let _ = Reflect::set(
1047 self.get_context(),
1048 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_OFFSET_Y),
1049 &JsValue::from_f64(config.get_offset_y()),
1050 );
1051 }
1052
1053 pub fn clear_shadow(&self) {
1055 let _ = Reflect::set(
1056 self.get_context(),
1057 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_COLOR),
1058 &JsValue::from_str("rgba(0, 0, 0, 0)"),
1059 );
1060 let _ = Reflect::set(
1061 self.get_context(),
1062 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_BLUR),
1063 &JsValue::from_f64(0.0),
1064 );
1065 let _ = Reflect::set(
1066 self.get_context(),
1067 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_OFFSET_X),
1068 &JsValue::from_f64(0.0),
1069 );
1070 let _ = Reflect::set(
1071 self.get_context(),
1072 &JsValue::from_str(RENDERER_PROPERTY_SHADOW_OFFSET_Y),
1073 &JsValue::from_f64(0.0),
1074 );
1075 }
1076
1077 pub fn set_linear_gradient_fill(&self, gradient: &LinearGradient) {
1083 if let Some(canvas_gradient) = gradient.to_canvas_gradient(self.get_context()) {
1084 let _ = Reflect::set(
1085 self.get_context(),
1086 &JsValue::from_str(RENDERER_PROPERTY_FILL_STYLE),
1087 &canvas_gradient,
1088 );
1089 }
1090 }
1091
1092 pub fn set_radial_gradient_fill(&self, gradient: &RadialGradient) {
1098 if let Some(canvas_gradient) = gradient.to_canvas_gradient(self.get_context()) {
1099 let _ = Reflect::set(
1100 self.get_context(),
1101 &JsValue::from_str(RENDERER_PROPERTY_FILL_STYLE),
1102 &canvas_gradient,
1103 );
1104 }
1105 }
1106
1107 pub fn set_linear_gradient_stroke(&self, gradient: &LinearGradient) {
1113 if let Some(canvas_gradient) = gradient.to_canvas_gradient(self.get_context()) {
1114 let _ = Reflect::set(
1115 self.get_context(),
1116 &JsValue::from_str(RENDERER_PROPERTY_STROKE_STYLE),
1117 &canvas_gradient,
1118 );
1119 }
1120 }
1121
1122 pub fn set_radial_gradient_stroke(&self, gradient: &RadialGradient) {
1128 if let Some(canvas_gradient) = gradient.to_canvas_gradient(self.get_context()) {
1129 let _ = Reflect::set(
1130 self.get_context(),
1131 &JsValue::from_str(RENDERER_PROPERTY_STROKE_STYLE),
1132 &canvas_gradient,
1133 );
1134 }
1135 }
1136}
1137
1138impl RenderBackend for CanvasRenderer {
1141 fn clear(&self) {
1142 self.clear();
1143 }
1144
1145 fn clear_with_color(&self, color: &str) {
1146 self.clear_with_color(color);
1147 }
1148
1149 fn save(&self) {
1150 self.save();
1151 }
1152
1153 fn restore(&self) {
1154 self.restore();
1155 }
1156
1157 fn set_fill_color(&self, color: &str) {
1158 self.set_fill_color(color);
1159 }
1160
1161 fn set_stroke_color(&self, color: &str) {
1162 self.set_stroke_color(color);
1163 }
1164
1165 fn set_line_width(&self, width: f64) {
1166 self.set_line_width(width);
1167 }
1168
1169 fn set_global_alpha(&self, alpha: f64) {
1170 self.set_global_alpha(alpha);
1171 }
1172
1173 fn set_blend_mode(&self, mode: BlendMode) {
1174 self.set_blend_mode(mode);
1175 }
1176
1177 fn set_shadow(&self, config: &ShadowConfig) {
1178 self.set_shadow(config);
1179 }
1180
1181 fn clear_shadow(&self) {
1182 self.clear_shadow();
1183 }
1184
1185 fn fill_rect(&self, position: Vector2D, width: f64, height: f64) {
1186 self.fill_rect(position, width, height);
1187 }
1188
1189 fn stroke_rect(&self, position: Vector2D, width: f64, height: f64) {
1190 self.stroke_rect(position, width, height);
1191 }
1192
1193 fn fill_circle(&self, center: Vector2D, radius: f64) {
1194 self.fill_circle(center, radius);
1195 }
1196
1197 fn stroke_circle(&self, center: Vector2D, radius: f64) {
1198 self.stroke_circle(center, radius);
1199 }
1200
1201 fn draw_line(&self, start: Vector2D, end: Vector2D) {
1202 self.draw_line(start, end);
1203 }
1204
1205 fn fill_text(&self, text: &str, position: Vector2D) {
1206 self.fill_text(text, position);
1207 }
1208
1209 fn set_font(&self, font: &str) {
1210 self.set_font(font);
1211 }
1212
1213 fn draw_image(&self, image: &HtmlImageElement, position: Vector2D, width: f64, height: f64) {
1214 self.draw_image(image, position, width, height);
1215 }
1216
1217 fn set_linear_gradient_fill(&self, gradient: &LinearGradient) {
1218 self.set_linear_gradient_fill(gradient);
1219 }
1220
1221 fn set_radial_gradient_fill(&self, gradient: &RadialGradient) {
1222 self.set_radial_gradient_fill(gradient);
1223 }
1224}