Merge pull request #1448 from bungoboingo/fear/linear-gradients
Add linear gradient support to canvas widget
This commit is contained in:
commit
d222b5c8b0
47 changed files with 2234 additions and 971 deletions
|
|
@ -2,7 +2,7 @@ use std::{f32::consts::PI, time::Instant};
|
|||
|
||||
use iced::executor;
|
||||
use iced::widget::canvas::{
|
||||
self, Cache, Canvas, Cursor, Geometry, Path, Stroke,
|
||||
self, stroke, Cache, Canvas, Cursor, Geometry, Path, Stroke,
|
||||
};
|
||||
use iced::{
|
||||
Application, Command, Element, Length, Point, Rectangle, Settings,
|
||||
|
|
@ -52,11 +52,6 @@ impl Application for Arc {
|
|||
Command::none()
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
iced::time::every(std::time::Duration::from_millis(10))
|
||||
.map(|_| Message::Tick)
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
Canvas::new(self)
|
||||
.width(Length::Fill)
|
||||
|
|
@ -67,6 +62,11 @@ impl Application for Arc {
|
|||
fn theme(&self) -> Theme {
|
||||
Theme::Dark
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
iced::time::every(std::time::Duration::from_millis(10))
|
||||
.map(|_| Message::Tick)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> canvas::Program<Message> for Arc {
|
||||
|
|
@ -114,7 +114,7 @@ impl<Message> canvas::Program<Message> for Arc {
|
|||
frame.stroke(
|
||||
&path,
|
||||
Stroke {
|
||||
color: palette.text,
|
||||
style: stroke::Style::Solid(palette.text),
|
||||
width: 10.0,
|
||||
..Stroke::default()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use iced::executor;
|
||||
use iced::widget::canvas::{Cache, Cursor, Geometry, LineCap, Path, Stroke};
|
||||
use iced::widget::canvas::{
|
||||
stroke, Cache, Cursor, Geometry, LineCap, Path, Stroke,
|
||||
};
|
||||
use iced::widget::{canvas, container};
|
||||
use iced::{
|
||||
Application, Color, Command, Element, Length, Point, Rectangle, Settings,
|
||||
|
|
@ -24,9 +26,9 @@ enum Message {
|
|||
}
|
||||
|
||||
impl Application for Clock {
|
||||
type Executor = executor::Default;
|
||||
type Message = Message;
|
||||
type Theme = Theme;
|
||||
type Executor = executor::Default;
|
||||
type Flags = ();
|
||||
|
||||
fn new(_flags: ()) -> (Self, Command<Message>) {
|
||||
|
|
@ -59,15 +61,6 @@ impl Application for Clock {
|
|||
Command::none()
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
iced::time::every(std::time::Duration::from_millis(500)).map(|_| {
|
||||
Message::Tick(
|
||||
time::OffsetDateTime::now_local()
|
||||
.unwrap_or_else(|_| time::OffsetDateTime::now_utc()),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
let canvas = canvas(self as &Self)
|
||||
.width(Length::Fill)
|
||||
|
|
@ -79,6 +72,15 @@ impl Application for Clock {
|
|||
.padding(20)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
iced::time::every(std::time::Duration::from_millis(500)).map(|_| {
|
||||
Message::Tick(
|
||||
time::OffsetDateTime::now_local()
|
||||
.unwrap_or_else(|_| time::OffsetDateTime::now_utc()),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> canvas::Program<Message> for Clock {
|
||||
|
|
@ -104,33 +106,41 @@ impl<Message> canvas::Program<Message> for Clock {
|
|||
let long_hand =
|
||||
Path::line(Point::ORIGIN, Point::new(0.0, -0.8 * radius));
|
||||
|
||||
let thin_stroke = Stroke {
|
||||
width: radius / 100.0,
|
||||
color: Color::WHITE,
|
||||
line_cap: LineCap::Round,
|
||||
..Stroke::default()
|
||||
let width = radius / 100.0;
|
||||
|
||||
let thin_stroke = || -> Stroke {
|
||||
Stroke {
|
||||
width,
|
||||
style: stroke::Style::Solid(Color::WHITE),
|
||||
line_cap: LineCap::Round,
|
||||
..Stroke::default()
|
||||
}
|
||||
};
|
||||
|
||||
let wide_stroke = Stroke {
|
||||
width: thin_stroke.width * 3.0,
|
||||
..thin_stroke
|
||||
let wide_stroke = || -> Stroke {
|
||||
Stroke {
|
||||
width: width * 3.0,
|
||||
style: stroke::Style::Solid(Color::WHITE),
|
||||
line_cap: LineCap::Round,
|
||||
..Stroke::default()
|
||||
}
|
||||
};
|
||||
|
||||
frame.translate(Vector::new(center.x, center.y));
|
||||
|
||||
frame.with_save(|frame| {
|
||||
frame.rotate(hand_rotation(self.now.hour(), 12));
|
||||
frame.stroke(&short_hand, wide_stroke);
|
||||
frame.stroke(&short_hand, wide_stroke());
|
||||
});
|
||||
|
||||
frame.with_save(|frame| {
|
||||
frame.rotate(hand_rotation(self.now.minute(), 60));
|
||||
frame.stroke(&long_hand, wide_stroke);
|
||||
frame.stroke(&long_hand, wide_stroke());
|
||||
});
|
||||
|
||||
frame.with_save(|frame| {
|
||||
frame.rotate(hand_rotation(self.now.second(), 60));
|
||||
frame.stroke(&long_hand, thin_stroke);
|
||||
frame.stroke(&long_hand, thin_stroke());
|
||||
})
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
[package]
|
||||
name = "geometry"
|
||||
version = "0.1.0"
|
||||
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../.." }
|
||||
iced_native = { path = "../../native" }
|
||||
iced_graphics = { path = "../../graphics" }
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
## Geometry
|
||||
|
||||
A custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../../wgpu).
|
||||
|
||||
The __[`main`]__ file contains all the code of the example.
|
||||
|
||||
<div align="center">
|
||||
<a href="https://gfycat.com/activeunfitkangaroo">
|
||||
<img src="https://thumbs.gfycat.com/ActiveUnfitKangaroo-small.gif">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
You can run it with `cargo run`:
|
||||
```
|
||||
cargo run --package geometry
|
||||
```
|
||||
|
||||
[`main`]: src/main.rs
|
||||
|
|
@ -1,217 +0,0 @@
|
|||
//! This example showcases a simple native custom widget that renders using
|
||||
//! arbitrary low-level geometry.
|
||||
mod rainbow {
|
||||
// For now, to implement a custom native widget you will need to add
|
||||
// `iced_native` and `iced_wgpu` to your dependencies.
|
||||
//
|
||||
// Then, you simply need to define your widget type and implement the
|
||||
// `iced_native::Widget` trait with the `iced_wgpu::Renderer`.
|
||||
//
|
||||
// Of course, you can choose to make the implementation renderer-agnostic,
|
||||
// if you wish to, by creating your own `Renderer` trait, which could be
|
||||
// implemented by `iced_wgpu` and other renderers.
|
||||
use iced_graphics::renderer::{self, Renderer};
|
||||
use iced_graphics::{Backend, Primitive};
|
||||
|
||||
use iced_native::widget::{self, Widget};
|
||||
use iced_native::{
|
||||
layout, Element, Layout, Length, Point, Rectangle, Size, Vector,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Rainbow;
|
||||
|
||||
impl Rainbow {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rainbow() -> Rainbow {
|
||||
Rainbow
|
||||
}
|
||||
|
||||
impl<Message, B, T> Widget<Message, Renderer<B, T>> for Rainbow
|
||||
where
|
||||
B: Backend,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
Length::Fill
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
_renderer: &Renderer<B, T>,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let size = limits.width(Length::Fill).resolve(Size::ZERO);
|
||||
|
||||
layout::Node::new(Size::new(size.width, size.width))
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
_tree: &widget::Tree,
|
||||
renderer: &mut Renderer<B, T>,
|
||||
_theme: &T,
|
||||
_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
use iced_graphics::triangle::{Mesh2D, Vertex2D};
|
||||
use iced_native::Renderer as _;
|
||||
|
||||
let b = layout.bounds();
|
||||
|
||||
// R O Y G B I V
|
||||
let color_r = [1.0, 0.0, 0.0, 1.0];
|
||||
let color_o = [1.0, 0.5, 0.0, 1.0];
|
||||
let color_y = [1.0, 1.0, 0.0, 1.0];
|
||||
let color_g = [0.0, 1.0, 0.0, 1.0];
|
||||
let color_gb = [0.0, 1.0, 0.5, 1.0];
|
||||
let color_b = [0.0, 0.2, 1.0, 1.0];
|
||||
let color_i = [0.5, 0.0, 1.0, 1.0];
|
||||
let color_v = [0.75, 0.0, 0.5, 1.0];
|
||||
|
||||
let posn_center = {
|
||||
if b.contains(cursor_position) {
|
||||
[cursor_position.x - b.x, cursor_position.y - b.y]
|
||||
} else {
|
||||
[b.width / 2.0, b.height / 2.0]
|
||||
}
|
||||
};
|
||||
|
||||
let posn_tl = [0.0, 0.0];
|
||||
let posn_t = [b.width / 2.0, 0.0];
|
||||
let posn_tr = [b.width, 0.0];
|
||||
let posn_r = [b.width, b.height / 2.0];
|
||||
let posn_br = [b.width, b.height];
|
||||
let posn_b = [(b.width / 2.0), b.height];
|
||||
let posn_bl = [0.0, b.height];
|
||||
let posn_l = [0.0, b.height / 2.0];
|
||||
|
||||
let mesh = Primitive::Mesh2D {
|
||||
size: b.size(),
|
||||
buffers: Mesh2D {
|
||||
vertices: vec![
|
||||
Vertex2D {
|
||||
position: posn_center,
|
||||
color: [1.0, 1.0, 1.0, 1.0],
|
||||
},
|
||||
Vertex2D {
|
||||
position: posn_tl,
|
||||
color: color_r,
|
||||
},
|
||||
Vertex2D {
|
||||
position: posn_t,
|
||||
color: color_o,
|
||||
},
|
||||
Vertex2D {
|
||||
position: posn_tr,
|
||||
color: color_y,
|
||||
},
|
||||
Vertex2D {
|
||||
position: posn_r,
|
||||
color: color_g,
|
||||
},
|
||||
Vertex2D {
|
||||
position: posn_br,
|
||||
color: color_gb,
|
||||
},
|
||||
Vertex2D {
|
||||
position: posn_b,
|
||||
color: color_b,
|
||||
},
|
||||
Vertex2D {
|
||||
position: posn_bl,
|
||||
color: color_i,
|
||||
},
|
||||
Vertex2D {
|
||||
position: posn_l,
|
||||
color: color_v,
|
||||
},
|
||||
],
|
||||
indices: vec![
|
||||
0, 1, 2, // TL
|
||||
0, 2, 3, // T
|
||||
0, 3, 4, // TR
|
||||
0, 4, 5, // R
|
||||
0, 5, 6, // BR
|
||||
0, 6, 7, // B
|
||||
0, 7, 8, // BL
|
||||
0, 8, 1, // L
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
renderer.with_translation(Vector::new(b.x, b.y), |renderer| {
|
||||
renderer.draw_primitive(mesh);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, B, T> From<Rainbow> for Element<'a, Message, Renderer<B, T>>
|
||||
where
|
||||
B: Backend,
|
||||
{
|
||||
fn from(rainbow: Rainbow) -> Self {
|
||||
Self::new(rainbow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use iced::widget::{column, container, scrollable};
|
||||
use iced::{Alignment, Element, Length, Sandbox, Settings};
|
||||
use rainbow::rainbow;
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
Example::run(Settings::default())
|
||||
}
|
||||
|
||||
struct Example;
|
||||
|
||||
impl Sandbox for Example {
|
||||
type Message = ();
|
||||
|
||||
fn new() -> Self {
|
||||
Example
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("Custom 2D geometry - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, _: ()) {}
|
||||
|
||||
fn view(&self) -> Element<()> {
|
||||
let content = column![
|
||||
rainbow(),
|
||||
"In this example we draw a custom widget Rainbow, using \
|
||||
the Mesh2D primitive. This primitive supplies a list of \
|
||||
triangles, expressed as vertices and indices.",
|
||||
"Move your cursor over it, and see the center vertex \
|
||||
follow you!",
|
||||
"Every Vertex2D defines its own color. You could use the \
|
||||
Mesh2D primitive to render virtually any two-dimensional \
|
||||
geometry for your widget.",
|
||||
]
|
||||
.padding(20)
|
||||
.spacing(20)
|
||||
.max_width(500)
|
||||
.align_items(Alignment::Start);
|
||||
|
||||
let scrollable =
|
||||
scrollable(container(content).width(Length::Fill).center_x());
|
||||
|
||||
container(scrollable)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_y()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
10
examples/modern_art/Cargo.toml
Normal file
10
examples/modern_art/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "modern_art"
|
||||
version = "0.1.0"
|
||||
authors = ["Bingus <shankern@protonmail.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
|
||||
rand = "0.8.5"
|
||||
140
examples/modern_art/src/main.rs
Normal file
140
examples/modern_art/src/main.rs
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
use iced::widget::canvas::{
|
||||
self, gradient::Location, gradient::Position, Cache, Canvas, Cursor, Frame,
|
||||
Geometry, Gradient,
|
||||
};
|
||||
use iced::{
|
||||
executor, Application, Color, Command, Element, Length, Point, Rectangle,
|
||||
Renderer, Settings, Size, Theme,
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
fn main() -> iced::Result {
|
||||
ModernArt::run(Settings {
|
||||
antialiasing: true,
|
||||
..Settings::default()
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Message {}
|
||||
|
||||
struct ModernArt {
|
||||
cache: Cache,
|
||||
}
|
||||
|
||||
impl Application for ModernArt {
|
||||
type Executor = executor::Default;
|
||||
type Message = Message;
|
||||
type Theme = Theme;
|
||||
type Flags = ();
|
||||
|
||||
fn new(_flags: Self::Flags) -> (Self, Command<Self::Message>) {
|
||||
(
|
||||
ModernArt {
|
||||
cache: Default::default(),
|
||||
},
|
||||
Command::none(),
|
||||
)
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("Modern Art")
|
||||
}
|
||||
|
||||
fn update(&mut self, _message: Message) -> Command<Message> {
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<'_, Self::Message, Renderer<Self::Theme>> {
|
||||
Canvas::new(self)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> canvas::Program<Message> for ModernArt {
|
||||
type State = ();
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Self::State,
|
||||
_theme: &Theme,
|
||||
bounds: Rectangle,
|
||||
_cursor: Cursor,
|
||||
) -> Vec<Geometry> {
|
||||
let geometry = self.cache.draw(bounds.size(), |frame| {
|
||||
let num_squares = thread_rng().gen_range(0..1200);
|
||||
|
||||
let mut i = 0;
|
||||
while i <= num_squares {
|
||||
generate_box(frame, bounds.size());
|
||||
i += 1;
|
||||
}
|
||||
});
|
||||
|
||||
vec![geometry]
|
||||
}
|
||||
}
|
||||
|
||||
fn random_direction() -> Location {
|
||||
match thread_rng().gen_range(0..8) {
|
||||
0 => Location::TopLeft,
|
||||
1 => Location::Top,
|
||||
2 => Location::TopRight,
|
||||
3 => Location::Right,
|
||||
4 => Location::BottomRight,
|
||||
5 => Location::Bottom,
|
||||
6 => Location::BottomLeft,
|
||||
7 => Location::Left,
|
||||
_ => Location::TopLeft,
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_box(frame: &mut Frame, bounds: Size) -> bool {
|
||||
let solid = rand::random::<bool>();
|
||||
|
||||
let random_color = || -> Color {
|
||||
Color::from_rgb(
|
||||
thread_rng().gen_range(0.0..1.0),
|
||||
thread_rng().gen_range(0.0..1.0),
|
||||
thread_rng().gen_range(0.0..1.0),
|
||||
)
|
||||
};
|
||||
|
||||
let gradient = |top_left: Point, size: Size| -> Gradient {
|
||||
let mut builder = Gradient::linear(Position::Relative {
|
||||
top_left,
|
||||
size,
|
||||
start: random_direction(),
|
||||
end: random_direction(),
|
||||
});
|
||||
let stops = thread_rng().gen_range(1..15u32);
|
||||
|
||||
let mut i = 0;
|
||||
while i <= stops {
|
||||
builder = builder.add_stop(i as f32 / stops as f32, random_color());
|
||||
i += 1;
|
||||
}
|
||||
|
||||
builder.build().unwrap()
|
||||
};
|
||||
|
||||
let top_left = Point::new(
|
||||
thread_rng().gen_range(0.0..bounds.width),
|
||||
thread_rng().gen_range(0.0..bounds.height),
|
||||
);
|
||||
|
||||
let size = Size::new(
|
||||
thread_rng().gen_range(50.0..200.0),
|
||||
thread_rng().gen_range(50.0..200.0),
|
||||
);
|
||||
|
||||
if solid {
|
||||
frame.fill_rectangle(top_left, size, random_color());
|
||||
} else {
|
||||
frame.fill_rectangle(top_left, size, gradient(top_left, size));
|
||||
};
|
||||
|
||||
solid
|
||||
}
|
||||
|
|
@ -2,7 +2,8 @@
|
|||
//! a circle around each fingertip. This only works on touch-enabled
|
||||
//! computers like Microsoft Surface.
|
||||
use iced::widget::canvas::event;
|
||||
use iced::widget::canvas::{self, Canvas, Cursor, Geometry, Stroke};
|
||||
use iced::widget::canvas::stroke::{self, Stroke};
|
||||
use iced::widget::canvas::{self, Canvas, Cursor, Geometry};
|
||||
use iced::{
|
||||
executor, touch, window, Application, Color, Command, Element, Length,
|
||||
Point, Rectangle, Settings, Subscription, Theme,
|
||||
|
|
@ -186,7 +187,7 @@ impl canvas::Program<Message> for State {
|
|||
frame.stroke(
|
||||
&path,
|
||||
Stroke {
|
||||
color: Color::BLACK,
|
||||
style: stroke::Style::Solid(Color::BLACK),
|
||||
width: 3.0,
|
||||
..Stroke::default()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ use iced::executor;
|
|||
use iced::theme::{self, Theme};
|
||||
use iced::time;
|
||||
use iced::widget::canvas;
|
||||
use iced::widget::canvas::{Cursor, Path, Stroke};
|
||||
use iced::widget::canvas::gradient::{self, Gradient};
|
||||
use iced::widget::canvas::stroke::{self, Stroke};
|
||||
use iced::widget::canvas::{Cursor, Path};
|
||||
use iced::window;
|
||||
use iced::{
|
||||
Application, Color, Command, Element, Length, Point, Rectangle, Settings,
|
||||
|
|
@ -37,9 +39,9 @@ enum Message {
|
|||
}
|
||||
|
||||
impl Application for SolarSystem {
|
||||
type Executor = executor::Default;
|
||||
type Message = Message;
|
||||
type Theme = Theme;
|
||||
type Executor = executor::Default;
|
||||
type Flags = ();
|
||||
|
||||
fn new(_flags: ()) -> (Self, Command<Message>) {
|
||||
|
|
@ -65,10 +67,6 @@ impl Application for SolarSystem {
|
|||
Command::none()
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
time::every(std::time::Duration::from_millis(10)).map(Message::Tick)
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
canvas(&self.state)
|
||||
.width(Length::Fill)
|
||||
|
|
@ -86,6 +84,10 @@ impl Application for SolarSystem {
|
|||
text_color: Color::WHITE,
|
||||
})
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
time::every(time::Duration::from_millis(10)).map(Message::Tick)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -178,8 +180,10 @@ impl<Message> canvas::Program<Message> for State {
|
|||
frame.stroke(
|
||||
&orbit,
|
||||
Stroke {
|
||||
style: stroke::Style::Solid(Color::from_rgba8(
|
||||
0, 153, 255, 0.1,
|
||||
)),
|
||||
width: 1.0,
|
||||
color: Color::from_rgba8(0, 153, 255, 0.1),
|
||||
line_dash: canvas::LineDash {
|
||||
offset: 0,
|
||||
segments: &[3.0, 6.0],
|
||||
|
|
@ -198,15 +202,18 @@ impl<Message> canvas::Program<Message> for State {
|
|||
frame.translate(Vector::new(Self::ORBIT_RADIUS, 0.0));
|
||||
|
||||
let earth = Path::circle(Point::ORIGIN, Self::EARTH_RADIUS);
|
||||
let shadow = Path::rectangle(
|
||||
Point::new(0.0, -Self::EARTH_RADIUS),
|
||||
Size::new(
|
||||
Self::EARTH_RADIUS * 4.0,
|
||||
Self::EARTH_RADIUS * 2.0,
|
||||
),
|
||||
);
|
||||
|
||||
frame.fill(&earth, Color::from_rgb8(0x6B, 0x93, 0xD6));
|
||||
let earth_fill =
|
||||
Gradient::linear(gradient::Position::Absolute {
|
||||
start: Point::new(-Self::EARTH_RADIUS, 0.0),
|
||||
end: Point::new(Self::EARTH_RADIUS, 0.0),
|
||||
})
|
||||
.add_stop(0.2, Color::from_rgb(0.15, 0.50, 1.0))
|
||||
.add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47))
|
||||
.build()
|
||||
.expect("Build Earth fill gradient");
|
||||
|
||||
frame.fill(&earth, earth_fill);
|
||||
|
||||
frame.with_save(|frame| {
|
||||
frame.rotate(rotation * 10.0);
|
||||
|
|
@ -215,14 +222,6 @@ impl<Message> canvas::Program<Message> for State {
|
|||
let moon = Path::circle(Point::ORIGIN, Self::MOON_RADIUS);
|
||||
frame.fill(&moon, Color::WHITE);
|
||||
});
|
||||
|
||||
frame.fill(
|
||||
&shadow,
|
||||
Color {
|
||||
a: 0.7,
|
||||
..Color::BLACK
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue