Skip to content

Commit

Permalink
feat: responsive canvas and grid on canvas
Browse files Browse the repository at this point in the history
  • Loading branch information
CalliEve committed May 27, 2024
1 parent 2828dd7 commit 623c471
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 7 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ edition = "2021"

[dependencies]
leptos = { version = "0.6.11", features = ["csr", "nightly"] }
web-sys = { version = "0.3", features = ["HtmlCanvasElement", "CanvasRenderingContext2d", "CssStyleDeclaration", "Element", "Window"] }
wasm-bindgen = { version = "0.2" }
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
<title>Algorithmically-Assisted Metro Map Design</title>
</head>

<body class="h-full"></body>
<body></body>
</html>
40 changes: 40 additions & 0 deletions src/algorithm/grid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use wasm_bindgen::JsValue;
use web_sys::CanvasRenderingContext2d;

pub fn draw_grid(canvas: &CanvasRenderingContext2d, size: (u32, u32), square_size: u32) {
canvas.set_line_width(1.0);
canvas.set_stroke_style(&JsValue::from_str("grey"));

draw_vertical_lines(canvas, size.0, square_size, size.1 / square_size);
draw_horizontal_lines(canvas, size.1, square_size, size.0 / square_size);
}

fn draw_vertical_lines(
canvas: &CanvasRenderingContext2d,
length: u32,
square_size: u32,
count: u32,
) {
for i in 0..count {
let x = (i * square_size + square_size) as f64;
canvas.begin_path();
canvas.move_to(x, 0.0);
canvas.line_to(x, length as f64);
canvas.stroke();
}
}

fn draw_horizontal_lines(
canvas: &CanvasRenderingContext2d,
length: u32,
square_size: u32,
count: u32,
) {
for i in 0..count {
let y = (i * square_size + square_size) as f64;
canvas.begin_path();
canvas.move_to(0.0, y);
canvas.line_to(length as f64, y);
canvas.stroke();
}
}
17 changes: 17 additions & 0 deletions src/algorithm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use wasm_bindgen::JsCast;
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};

mod grid;

use grid::draw_grid;

pub fn redraw_canvas(canvas: &HtmlCanvasElement, size: (u32, u32)) {
let context = canvas
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<CanvasRenderingContext2d>()
.unwrap();

draw_grid(&context, size, 30);
}
70 changes: 68 additions & 2 deletions src/components/canvas.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,76 @@
use leptos::html::Canvas;
use leptos::logging::log;
use leptos::*;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;

use crate::algorithm::redraw_canvas;

fn redraw(canvas_node: &HtmlElement<Canvas>) {
// To have a canvas resize dynamically, we need to manually adjust its size
// CSS will NOT work, as it will just make everything blurry
let doc = window().document().expect("should have document");
let win_height = window().inner_height().unwrap().as_f64().unwrap();
let win_width = window().inner_width().unwrap().as_f64().unwrap();

// the navbar borders the top, so the height is `window - navbar`
let nav = doc
.get_element_by_id("navbar")
.expect("navbar should exist");
let nav_height_px = window()
.get_computed_style(&nav)
.unwrap()
.expect("should have style")
.get_property_value("height")
.expect("should have height property");

let height = (win_height
- nav_height_px
.trim_end_matches("px")
.parse::<f64>()
.expect("height should be an integer")) as u32;
canvas_node.set_height(height);

// the sidebar borders its side, so width is `window - sidebar`
let side = doc
.get_element_by_id("sidebar")
.expect("sidebar should exist");
let side_width_px = window()
.get_computed_style(&side)
.unwrap()
.expect("should have style")
.get_property_value("width")
.expect("should have width property");

let width = (win_width
- side_width_px
.trim_end_matches("px")
.parse::<f64>()
.expect("width should be an integer")) as u32;
canvas_node.set_width(width);

// Now the canvas is the correct size, we can draw it
log!("redrawing canvas");
redraw_canvas(&*canvas_node, (height, width));
}

#[component]
pub fn Canvas() -> impl IntoView {
let canvas_ref = create_node_ref::<Canvas>();

create_effect(move |_| {
let canvas_node = canvas_ref.get().expect("should be loaded now");

redraw(&canvas_node);

let f = Closure::<dyn Fn()>::new(move || redraw(&canvas_node));
window().set_onresize(Some(f.as_ref().unchecked_ref()));
f.forget();
});

view! {
<div class="h-full w-full flex bg-zinc-50 dark:bg-neutral-700 text-black dark:text-white">
<canvas id="canvas" class="grow m-5"/>
<div class="grow overflow-hidden bg-zinc-50 dark:bg-neutral-700 text-black dark:text-white">
<canvas _ref=canvas_ref id="canvas" class="object-contain"/>
</div>
}
}
2 changes: 1 addition & 1 deletion src/components/navbar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use leptos::*;
#[component]
pub fn Navbar() -> impl IntoView {
view! {
<nav class="relative flex w-full items-center justify-between bg-zinc-100 py-2 shadow-dark-mild shadow-sm dark:shadow-neutral-900 dark:bg-neutral-750 lg:py-4">
<nav id="navbar" class="relative flex w-full items-center justify-between bg-zinc-100 py-2 shadow-dark-mild shadow-sm dark:shadow-neutral-900 dark:bg-neutral-750 lg:py-4">
<div class="flex w-full items-center justify-between px-3">
<div class="ms-2">
<a class="text-2xl font-extrabold text-black dark:text-white" href="#">Metro Map Editor</a>
Expand Down
2 changes: 1 addition & 1 deletion src/components/sidebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use leptos::*;
#[component]
pub fn Sidebar() -> impl IntoView {
view! {
<div class="h-full w-full flex flex-col bg-zinc-100 py-2 shadow-right shadow-dark-mild dark:shadow-black dark:bg-neutral-750 text-black dark:text-white px-2">
<div id="sidebar" class="h-full w-full flex flex-col bg-zinc-100 py-2 shadow-right shadow-dark-mild dark:shadow-black dark:bg-neutral-750 text-black dark:text-white px-2">
<div class="px-3 py-3 w-full">sidebar</div>
</div>
}
Expand Down
5 changes: 3 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use leptos::*;

mod algorithm;
mod components;

pub use components::*;
Expand All @@ -11,15 +12,15 @@ fn main() {
#[component]
fn App() -> impl IntoView {
view! {
<div class="flex flex-col h-full">
<div class="flex flex-col h-screen max-w-screen">
<header>
<Navbar/>
</header>
<div class="grow flex flex-row justify-start">
<div class="flex-none self-start self-stretch w-1/5 md:w-52">
<Sidebar/>
</div>
<div class="grow self-stretch">
<div class="grow flex self-stretch">
<Canvas/>
</div>
</div>
Expand Down

0 comments on commit 623c471

Please sign in to comment.