# 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
```rust
# 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
| Pivot | Position |
|--------|----------|
| `CENTER` | 50%, 50% (default) |
| `TOP_LEFT` | 0%, 0% |
| `TOP_RIGHT` | 100%, 0% |
| `BOTTOM_LEFT` | 0%, 100% |
| `BOTTOM_RIGHT` | 100%, 100% |
| `TOP` | 50%, 0% |
| `BOTTOM` | 50%, 100% |
| `LEFT` | 0%, 50% |
| `RIGHT` | 100%, 50% |
## Visual Examples
### Rotation from Different Origins
```text
CENTER (default): TOP_LEFT:
┌───┐ ┌───┐
│ ↻ │ ↻
└───┘
BOTTOM_RIGHT:
┌───┐
│ │↻
└───┘
```
## Examples
### Rotate from Top-Left
```rust
# 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
```rust
# extern crate guido;
# use guido::prelude::*;
# fn main() {
container()
.scale(1.5)
.pivot(Pivot::BOTTOM_RIGHT)
# ;
# }
```
### Pivot from Top Edge
```rust
# extern crate guido;
# use guido::prelude::*;
# fn main() {
container()
.rotate(15.0)
.pivot(Pivot::TOP)
# ;
# }
```
## Custom Origin
Specify exact percentages:
```rust
# 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:
```rust
# 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
```rust
# 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
```rust,ignore
# // 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;
}
```