Yew框架实现秒表
实现计数器
用Yew框架实现一个web定时器,首先Interval实现一个简单的计数器,每10ms发送Msg UpdateTime并组件的update方法中调用tick方法,更新时间。
use gloo::timers::callback::Interval;
use yew::{html, Component, Context, Html};
pub enum Msg {
UpdateTime,
}
pub struct MyTimer {
timer_min: u32,
timer_sec: u32,
timer_csec: u32,
timer_handle: Option<Interval>,
}
impl MyTimer {
fn tick(&mut self) {
self.timer_csec += 1;
if self.timer_csec >= 100{
self.timer_csec = 0;
self.timer_sec += 1;
if self.timer_sec == 60 {
self.timer_sec = 0;
self.timer_min += 1;
}
}
}
}
impl Component for MyTimer {
type Message = Msg;
type Properties = ();
fn create(ctx: &Context<Self>) -> Self {
let timer_handle = {
let link = ctx.link().clone();
Interval::new(10, move || link.send_message(Msg::UpdateTime))
};
let timer_handle = Some(timer_handle);
Self {
timer_min: 0,
timer_sec: 0,
timer_csec: 0,
timer_handle,
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::UpdateTime => {
self.tick();
true
}
}
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
<>
<div id="time">
{ format!("{:0>2}:{:0>2}:{:0>2}",&self.timer_min,&self.timer_sec,&self.timer_csec) }
</div>
</>
}
}
}
实现秒表功能
秒表需要实现开始、停止、继续、计次、重置五个功能。因为开始和继续都是初始化定时器,因此可以用同一个Msg表示,在Msg
中添加这4个消息。
pub enum Msg {
StartTime,
StopTime,
CountTime,
ResetTime,
UpdateTime,
}
开始
默认秒表应该处于停止状态,点击开始按钮,发送StartTime
消息后,开始计数。因此需要将Interval
的初始化放到update
方法中。在create
方法中用None
初始化timer_handle
,在update
中接收到StartTime
消息后进行初始化。
match msg {
Msg::StartTime => {
let timer_handle = {
let link = ctx.link().clone();
Interval::new(10, move || link.send_message(Msg::UpdateTime))
};
self.timer_handle = Some(timer_handle);
true
}
// ...
}
停止
停止秒表直接将timer_handle重新赋值为None即可,这样就删除了定时器。
Msg::StopTime=>{
self.timer_handle = None;
true
}
继续
继续与开始的逻辑相同。
计次
计次需要一个变量来保存当前的时间,因此在结构体中增加一个可变数组用于保存当前时间。
Msg::CountTime => {
self.message.push(
format!("{:0>2}:{:0>2}:{:0>2}",&self.timer_min,&self.timer_sec,&self.timer_csec) }
);
true
}
重置
重置需要将所有计时器归零,并清空message中的计数。
Msg::ResetTime=>{
self.timer_csec = 0;
self.timer_sec = 0;
self.timer_min = 0;
self.message.clear();
true
}
到这里,秒表的基本功基本完全实现了,接下来将update方法中的代码整理到结构体为结构体的方法,添加一些变量来记录秒表的状态,另外添加一些css来优化界面。
代码整理
将代码逻辑进行一些整理优化
use gloo::timers::callback::Interval;
use yew::{html, Component, Context, Html};
pub enum Msg {
StartTime,
StopTime,
CountTime,
ResetTime,
UpdateTime,
}
pub struct MyTimer {
timer_csec: u32,
last_timer_csec: u32,
timer_handle: Option<Interval>,
message: Vec<(u32, u32)>,
state: u8,
}
impl MyTimer {
fn new(_ctx: &Context<Self>) -> Self {
Self {
timer_csec: 0,
last_timer_csec: 0,
timer_handle: None,
message: vec![],
state: 0,
}
}
fn start_time(&mut self, ctx: &Context<Self>) -> bool {
if self.state == 0 || self.state == 2 {
let timer_handle = {
let link = ctx.link().clone();
Interval::new(10, move || link.send_message(Msg::UpdateTime))
};
self.timer_handle = Some(timer_handle);
self.state = 1;
}
true
}
fn stop_time(&mut self) -> bool {
if self.state == 1 {
self.timer_handle = None;
self.state = 2;
}
true
}
fn conut_time(&mut self) -> bool {
if self.state == 1 {
self.message
.push((self.timer_csec, self.timer_csec - self.last_timer_csec));
self.last_timer_csec = self.timer_csec;
}
true
}
fn reset_time(&mut self) -> bool {
if self.state == 2 {
self.timer_csec = 0;
self.last_timer_csec = 0;
self.message.clear();
self.state = 0;
}
true
}
fn tick(&mut self) -> bool {
self.timer_csec += 1;
true
}
}
impl Component for MyTimer {
type Message = Msg;
type Properties = ();
fn create(ctx: &Context<Self>) -> Self {
MyTimer::new(ctx)
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::StartTime => self.start_time(ctx),
Msg::StopTime => self.stop_time(),
Msg::CountTime => self.conut_time(),
Msg::ResetTime => self.reset_time(),
Msg::UpdateTime => self.tick(),
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let mut button1 = html! {
<button onclick={ctx.link().callback(|_| Msg::CountTime)}>{"计次-"}</button>
};
let mut button2 = html! {
<button onclick={ctx.link().callback(|_| Msg::StartTime)}>{"开始"}</button>
};
if self.state == 1 {
button1 = html! {
<button onclick={ctx.link().callback(|_| Msg::CountTime)}>{"计次"}</button>
};
button2 = html! {
<button onclick={ctx.link().callback(|_| Msg::StopTime)}>{"停止"}</button>
};
} else if self.state == 2 {
button1 = html! {
<button onclick={ctx.link().callback(|_| Msg::ResetTime)}>{"重置"}</button>
};
button2 = html! {
<button onclick={ctx.link().callback(|_| Msg::StartTime)}>{"继续"}</button>
};
}
html! {
<>
<div id="time">
{ format!("{:0>2}:{:0>2}.{:0>2}",&self.timer_csec/6000,(&self.timer_csec%6000)/100,&self.timer_csec%100) }
</div>
<div id="buttons">
{ button1 }
{ button2 }
</div>
<div id="message">
{ for self.message.iter().map(|m| html!{ <li> {format!("{:#?}",m)} </li> })}
</div>
</>
}
}
}