Skip to content

Transforms lag significantly sooner than in webview #595

Description

@Klemen2

When transforming large number of children blitz tend to lag at a way smaller amount than the webview
Platform tested: windows 11

Blitz release count: 200

blitz.mp4

Webview release count: 3000

web.mp4
use std::collections::HashMap;

use dioxus_native::prelude::dioxus_elements::geometry::ClientPoint;
use dioxus_native::prelude::*;

const COUNT: usize = 200;

pub fn main() {
    dioxus_native::launch(app);
}

fn app() -> Element {
    let mut viewport = use_store(Viewport::default);
    let data = use_store(Data::default);
    let mut position = use_store(|| ClientPoint::zero());

    let onpointermove = move |evt: Event<PointerData>| {
        let coords = evt.client_coordinates();
        viewport.write().pan(&coords, &position.peek());
        position.set(coords);
    };

    let onwheel = move |e: Event<WheelData>| {
        let coords = e.client_coordinates();
        let delta = e.delta().strip_units().y as f32;

        viewport
            .write()
            .onscroll(coords.x as f32, coords.y as f32, delta);
    };
    rsx!(
        style {
            {CSS}
        }
        div {
            tabindex: "-1",
            position: "absolute",
            top: 0,
            left: 0,
            width: "100vw",
            height:"100vh",
            onpointermove,
            onwheel,

            div {
                border: "1px solid black",
                position: "absolute",
                top: 0,
                left: 0,
                transform_origin: "0 0",
                width: 0,
                height: 0,
                transform: "{viewport}",
                div {
                    for (id, item) in data.elements().iter() {
                        PositionedItem{
                            id: id,
                            data: item
                        }
                    }
                }
            }
        }
    )
}

#[component]
fn PositionedItem(id: usize, data: Store<PositinedElement>) -> Element {
    rsx!(
        div {
            z_index: id,
            pointer_events: "none",
            user_select: "none",
            position: "absolute",
            border: "1px solid black",
            left: format!("{}px", data.x()),
            top: format!("{}px", data.y()),
            width: format!("{}px", data.width()),
            height: format!("{}px", data.height()),
            div {
                "Placed item {id}"
            }
        }
    )
}

#[derive(Store, Clone, PartialEq)]
struct PositinedElement {
    x: f32,
    y: f32,
    width: f32,
    height: f32,
}

#[derive(Store, PartialEq)]
struct Data {
    elements: HashMap<usize, PositinedElement>,
}

impl Default for Data {
    fn default() -> Self {
        Self {
            elements: (0..COUNT)
                .map(|i| {
                    (
                        i,
                        PositinedElement {
                            x: i as f32 * 30.0,
                            y: i as f32 * 20.0,
                            width: 60.0 + ((i * 37 + 11) % 100) as f32 * 0.8,
                            height: 60.0 + ((i * 53 + 7) % 100) as f32 * 0.8,
                        },
                    )
                })
                .collect(),
        }
    }
}

#[derive(Store, Debug, Clone, PartialEq)]
struct Viewport {
    pan_x: f32,
    pan_y: f32,
    zoom: f32,
}

impl std::fmt::Display for Viewport {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "translate({}px, {}px) scale({})",
            self.pan_x, self.pan_y, self.zoom
        )
    }
}

impl Default for Viewport {
    fn default() -> Self {
        Self {
            pan_x: Default::default(),
            pan_y: Default::default(),
            zoom: 1.0,
        }
    }
}

impl Viewport {
    fn pan(&mut self, screen: &ClientPoint, last: &ClientPoint) {
        let dx = screen.x - last.x;
        let dy = screen.y - last.y;
        self.pan_x += dx as f32;
        self.pan_y += dy as f32;
    }
    pub fn onscroll(&mut self, sx: f32, sy: f32, delta: f32) {
        let factor = (1.0 + delta).clamp(0.8, 1.2);

        let new_zoom = (self.zoom * factor).clamp(0.05, 50.0);

        let wx = (sx - self.pan_x) / self.zoom;
        let wy = (sy - self.pan_y) / self.zoom;

        self.pan_x = sx - wx * new_zoom;
        self.pan_y = sy - wy * new_zoom;
        self.zoom = new_zoom;
    }
}

const CSS: &str = r#"
* {
    box-sizing: border-box;
}

html,
body,
main {
    width: 100vw;
    height: 100vh;
    margin: 0;
}
"#;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions