Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Pivots

By default, rotation and scale happen about the widget’s centre. A pivot moves that point.

One pivot serves both rotate and scale, as CSS gives one transform-origin to the whole group. translate ignores it: moving a box is the same movement wherever the pivot sits.

Setting a Pivot

extern crate guido;
use guido::prelude::*;
fn main() {
container()
    .rotate(45.0)
    .pivot(Pivot::TOP_LEFT)
;
}

Now the container rotates around its top-left corner instead of its center.

Built-in Pivots

PivotPosition
CENTER50%, 50% (default)
TOP_LEFT0%, 0%
TOP_RIGHT100%, 0%
BOTTOM_LEFT0%, 100%
BOTTOM_RIGHT100%, 100%
TOP50%, 0%
BOTTOM50%, 100%
LEFT0%, 50%
RIGHT100%, 50%

Visual Examples

Rotation from Different Origins

CENTER (default):        TOP_LEFT:
    ┌───┐                ┌───┐
    │ ↻ │                ↻
    └───┘

BOTTOM_RIGHT:
                         ┌───┐
                         │   │↻
                         └───┘

Examples

Rotate from Top-Left

extern crate guido;
use guido::prelude::*;
fn main() {
container()
    .width(80.0)
    .height(80.0)
    .background(Color::rgb(0.3, 0.5, 0.8))
    .rotate(30.0)
    .pivot(Pivot::TOP_LEFT)
;
}

Scale from Bottom-Right

extern crate guido;
use guido::prelude::*;
fn main() {
container()
    .scale(1.5)
    .pivot(Pivot::BOTTOM_RIGHT)
;
}

Pivot from Top Edge

extern crate guido;
use guido::prelude::*;
fn main() {
container()
    .rotate(15.0)
    .pivot(Pivot::TOP)
;
}

Custom Origin

Specify exact percentages:

extern crate guido;
use guido::prelude::*;
fn main() {
// 25% from left, 75% from top
Pivot::percent(25.0, 75.0)
;
}

Values are percentages of the widget’s size:

  • 0.0 = left/top edge
  • 0.5 = center
  • 1.0 = right/bottom edge

Reactive Origins

Pivots can be reactive:

extern crate guido;
use guido::prelude::*;
fn main() {
let origin = create_signal(Pivot::CENTER);

container()
    .rotate(45.0)
    .pivot(origin)
    .on_click(move || {
        // Cycle through origins
        // `Pivot` carries f32 anchors, so its constants are not patterns —
        // compare, do not match.
        let current = origin.get();
        origin.set(if current == Pivot::CENTER {
            Pivot::TOP_LEFT
        } else if current == Pivot::TOP_LEFT {
            Pivot::BOTTOM_RIGHT
        } else {
            Pivot::CENTER
        });
    })
;
}

Complete Example

extern crate guido;
use guido::prelude::*;
fn origin_demo() -> impl Widget {
    container()
        .layout(Flex::row().spacing(40.0))
        .children([
            // Rotate from center (default)
            create_rotating_box(Pivot::CENTER, "Center"),

            // Rotate from top-left
            create_rotating_box(Pivot::TOP_LEFT, "Top-Left"),

            // Rotate from bottom-right
            create_rotating_box(Pivot::BOTTOM_RIGHT, "Bottom-Right"),
        ])
}

fn create_rotating_box(origin: Pivot, label: &'static str) -> Container {
    let rotation = create_signal(0.0f32);

    container()
        .layout(Flex::column().spacing(8.0))
        .children([
            container()
                .width(60.0)
                .height(60.0)
                .background(Color::rgb(0.3, 0.5, 0.8))
                .corners(8.0)
                .rotate(rotation.transition(Transition::new(300.0, TimingFunction::EaseOut)))
                .pivot(origin)
                .when_hovered(|s| s.lighter(0.1))
                .on_click(move || rotation.update(|r| *r += 45.0)),
            container().child(text(label).font_size(12.0).color(Color::WHITE)),
        ])
}
fn main() {}

API Reference

// not compiled: a signature listing — these declarations have no bodies.
impl Container {
    pub fn pivot(
        self,
        origin: impl IntoSignal<Pivot, M>
    ) -> Self;
}

impl Pivot {
    pub const CENTER: Pivot;
    pub const TOP_LEFT: Pivot;
    pub const TOP_RIGHT: Pivot;
    pub const BOTTOM_LEFT: Pivot;
    pub const BOTTOM_RIGHT: Pivot;
    pub const TOP: Pivot;
    pub const BOTTOM: Pivot;
    pub const LEFT: Pivot;
    pub const RIGHT: Pivot;

    pub fn percent(x_percent: f32, y_percent: f32) -> Pivot;  // 0-100
    pub fn px(x: f32, y: f32) -> Pivot;
}