242 lines
6.8 KiB
Rust
242 lines
6.8 KiB
Rust
|
|
/*!
|
|||
|
|
* TimerBox - JavaScript風タイマー機能Box
|
|||
|
|
*
|
|||
|
|
* ## 📝 概要
|
|||
|
|
* setTimeout/setInterval/requestAnimationFrameをNyashから利用可能にするBox。
|
|||
|
|
* アニメーション、遅延実行、定期実行を統一的に管理。
|
|||
|
|
*
|
|||
|
|
* ## 🛠️ 利用可能メソッド
|
|||
|
|
*
|
|||
|
|
* ### ⏱️ 基本タイマー
|
|||
|
|
* - `setTimeout(callback, delay)` - 指定時間後に1回実行
|
|||
|
|
* - `setInterval(callback, interval)` - 指定間隔で繰り返し実行
|
|||
|
|
* - `clearTimeout(id)` - タイマーをキャンセル
|
|||
|
|
* - `clearInterval(id)` - インターバルをキャンセル
|
|||
|
|
*
|
|||
|
|
* ### 🎮 アニメーション
|
|||
|
|
* - `requestAnimationFrame(callback)` - 次フレームで実行
|
|||
|
|
* - `cancelAnimationFrame(id)` - アニメーションをキャンセル
|
|||
|
|
*
|
|||
|
|
* ### 📊 時間測定
|
|||
|
|
* - `now()` - 現在時刻(ミリ秒)
|
|||
|
|
* - `performance()` - 高精度時刻測定
|
|||
|
|
*
|
|||
|
|
* ## 💡 使用例
|
|||
|
|
* ```nyash
|
|||
|
|
* local timer, id
|
|||
|
|
* timer = new TimerBox()
|
|||
|
|
*
|
|||
|
|
* // 1秒後に実行
|
|||
|
|
* id = timer.setTimeout(function() {
|
|||
|
|
* print("Hello after 1 second!")
|
|||
|
|
* }, 1000)
|
|||
|
|
*
|
|||
|
|
* // 500msごとに実行
|
|||
|
|
* id = timer.setInterval(function() {
|
|||
|
|
* print("Tick every 500ms")
|
|||
|
|
* }, 500)
|
|||
|
|
*
|
|||
|
|
* // アニメーションループ
|
|||
|
|
* timer.requestAnimationFrame(function() {
|
|||
|
|
* // 描画処理
|
|||
|
|
* canvas.clear()
|
|||
|
|
* canvas.drawRect(x, y, 50, 50)
|
|||
|
|
* })
|
|||
|
|
* ```
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
|||
|
|
use std::any::Any;
|
|||
|
|
|
|||
|
|
#[cfg(target_arch = "wasm32")]
|
|||
|
|
use wasm_bindgen::prelude::*;
|
|||
|
|
|
|||
|
|
#[cfg(target_arch = "wasm32")]
|
|||
|
|
use web_sys::{window, Performance};
|
|||
|
|
|
|||
|
|
/// タイマー管理Box
|
|||
|
|
#[derive(Debug, Clone)]
|
|||
|
|
pub struct TimerBox {
|
|||
|
|
base: BoxBase,
|
|||
|
|
#[cfg(target_arch = "wasm32")]
|
|||
|
|
performance: Option<Performance>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl TimerBox {
|
|||
|
|
pub fn new() -> Self {
|
|||
|
|
#[cfg(target_arch = "wasm32")]
|
|||
|
|
let performance = window().and_then(|w| w.performance().ok());
|
|||
|
|
|
|||
|
|
Self {
|
|||
|
|
base: BoxBase::new(),
|
|||
|
|
#[cfg(target_arch = "wasm32")]
|
|||
|
|
performance,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 現在時刻をミリ秒で取得
|
|||
|
|
pub fn now(&self) -> f64 {
|
|||
|
|
#[cfg(target_arch = "wasm32")]
|
|||
|
|
{
|
|||
|
|
if let Some(perf) = &self.performance {
|
|||
|
|
perf.now()
|
|||
|
|
} else {
|
|||
|
|
js_sys::Date::now()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|||
|
|
{
|
|||
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|||
|
|
SystemTime::now()
|
|||
|
|
.duration_since(UNIX_EPOCH)
|
|||
|
|
.unwrap_or_default()
|
|||
|
|
.as_millis() as f64
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 高精度時刻測定
|
|||
|
|
pub fn performance_now(&self) -> f64 {
|
|||
|
|
self.now()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(target_arch = "wasm32")]
|
|||
|
|
/// setTimeout相当の遅延実行
|
|||
|
|
pub fn set_timeout(&self, callback: &js_sys::Function, delay: i32) -> i32 {
|
|||
|
|
if let Some(window) = window() {
|
|||
|
|
window.set_timeout_with_callback_and_timeout_and_arguments_0(callback, delay)
|
|||
|
|
.unwrap_or(-1)
|
|||
|
|
} else {
|
|||
|
|
-1
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(target_arch = "wasm32")]
|
|||
|
|
/// setInterval相当の定期実行
|
|||
|
|
pub fn set_interval(&self, callback: &js_sys::Function, interval: i32) -> i32 {
|
|||
|
|
if let Some(window) = window() {
|
|||
|
|
window.set_interval_with_callback_and_timeout_and_arguments_0(callback, interval)
|
|||
|
|
.unwrap_or(-1)
|
|||
|
|
} else {
|
|||
|
|
-1
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(target_arch = "wasm32")]
|
|||
|
|
/// clearTimeout相当のタイマーキャンセル
|
|||
|
|
pub fn clear_timeout(&self, id: i32) {
|
|||
|
|
if let Some(window) = window() {
|
|||
|
|
window.clear_timeout_with_handle(id);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(target_arch = "wasm32")]
|
|||
|
|
/// clearInterval相当のインターバルキャンセル
|
|||
|
|
pub fn clear_interval(&self, id: i32) {
|
|||
|
|
if let Some(window) = window() {
|
|||
|
|
window.clear_interval_with_handle(id);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(target_arch = "wasm32")]
|
|||
|
|
/// requestAnimationFrame相当のアニメーション実行
|
|||
|
|
pub fn request_animation_frame(&self, callback: &js_sys::Function) -> i32 {
|
|||
|
|
if let Some(window) = window() {
|
|||
|
|
window.request_animation_frame(callback).unwrap_or(-1)
|
|||
|
|
} else {
|
|||
|
|
-1
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(target_arch = "wasm32")]
|
|||
|
|
/// cancelAnimationFrame相当のアニメーションキャンセル
|
|||
|
|
pub fn cancel_animation_frame(&self, id: i32) {
|
|||
|
|
if let Some(window) = window() {
|
|||
|
|
window.cancel_animation_frame(id).unwrap_or_default();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|||
|
|
/// Non-WASM環境用のダミー実装
|
|||
|
|
pub fn set_timeout(&self, _delay: i32) -> i32 {
|
|||
|
|
println!("TimerBox: setTimeout not supported in non-WASM environment");
|
|||
|
|
-1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|||
|
|
pub fn set_interval(&self, _interval: i32) -> i32 {
|
|||
|
|
println!("TimerBox: setInterval not supported in non-WASM environment");
|
|||
|
|
-1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|||
|
|
pub fn clear_timeout(&self, _id: i32) {
|
|||
|
|
println!("TimerBox: clearTimeout not supported in non-WASM environment");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|||
|
|
pub fn clear_interval(&self, _id: i32) {
|
|||
|
|
println!("TimerBox: clearInterval not supported in non-WASM environment");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|||
|
|
pub fn request_animation_frame(&self) -> i32 {
|
|||
|
|
println!("TimerBox: requestAnimationFrame not supported in non-WASM environment");
|
|||
|
|
-1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|||
|
|
pub fn cancel_animation_frame(&self, _id: i32) {
|
|||
|
|
println!("TimerBox: cancelAnimationFrame not supported in non-WASM environment");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl BoxCore for TimerBox {
|
|||
|
|
fn box_id(&self) -> u64 {
|
|||
|
|
self.base.id
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
|||
|
|
self.base.parent_type_id
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|||
|
|
write!(f, "TimerBox(id={})", self.base.id)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fn as_any(&self) -> &dyn Any {
|
|||
|
|
self
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
|||
|
|
self
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl NyashBox for TimerBox {
|
|||
|
|
fn clone_box(&self) -> Box<dyn NyashBox> {
|
|||
|
|
Box::new(self.clone())
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fn to_string_box(&self) -> StringBox {
|
|||
|
|
StringBox::new(format!("TimerBox(id={})", self.base.id))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fn type_name(&self) -> &'static str {
|
|||
|
|
"TimerBox"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
|||
|
|
if let Some(other_timer) = other.as_any().downcast_ref::<TimerBox>() {
|
|||
|
|
BoolBox::new(self.base.id == other_timer.base.id)
|
|||
|
|
} else {
|
|||
|
|
BoolBox::new(false)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl std::fmt::Display for TimerBox {
|
|||
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|||
|
|
self.fmt_box(f)
|
|||
|
|
}
|
|||
|
|
}
|