Skip to content
Snippets Groups Projects
Verified Commit e728eeab authored by Louis's avatar Louis :fire:
Browse files

Copy bindings

parents
No related branches found
No related tags found
No related merge requests found
/target
/Cargo.lock
.idea/
\ No newline at end of file
[package]
name = "micro_bevy_web_utils"
version = "0.1.0"
edition = "2021"
description = "Utilities for patching various missing parts from Bevy web/touch support"
authors = [
"Louis Capitanchik <louis@microhacks.co.uk>"
]
[dependencies]
bevy_ecs = "0.9.1"
bevy_input = "0.9.1"
bevy_math = "0.9.1"
wasm-bindgen = "0.2.83"
serde = "1.0.151"
serde_json = "1.0.91"
\ No newline at end of file
# Bevy Web Utils
Helpers for working with Bevy on the web
## Bindings
This crate includes `wasm_bindgen` bindings to some JS snippets that help with various
web tasks, that can be accessed from the `micro_bevy_web_utils::bindings` module
- ` orientation_lock(orientation: String);`: Locks the screen to the given orientation. Accepts values defined in the [Screen Orientation API](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Managing_screen_orientation#preventing_orientation_change). Noop on web platforms that do not support locking orientation
- ` orientation_unlock();`: Unlocks the screen orientation. Noop when the orientation is not locked, or on web platforms that do not support locking orientation
- ` make_selector_fullscreen(selector: String);`: Take a query selector, and requests fullscreen mode for the first matching element. Noop if the query selector does not match any elements. Calling `make_selector_fullscreen("canvas")` is the common usage
- ` toggle_selector_fullscreen(selector: String) -> bool;`: Take a query selector and either request fullscreen mode if the window is not in fullscreen, or close fullscreen mode if the window is fullscreen
- ` exit_fullscreen();`: Close fullscreen mode
- ` bind_selector_touch_events(selector: String);`: Bind touch events to the element. This is used in combination with the `micro_bevy_web_utils::bevy::emit_touch_events` system to send touch events on touch devices
- ` teardown_selector_touch_events(selector: String);`: Remove touch event bindings for a given element
- ` take_touch_events() -> String;`: Clears the touch event buffer and returns a serialised JSON array containing all of the events that have been recorded since the last call to `take_touch_events`
- ` is_fullscreen() -> bool;`: Returns whether an element has fullscreen mode enabled
- ` is_touch_device() -> bool;`: Returns whether the device supports touch input
## Bevy System
Touch events will only be intercepted after at least one successful call to `bind_selector_touch_events`
To dispatch touch events recorded by this library, you can add the `micro_bevy_web_utils::bevy::emit_touch_events` system. You will then receive
intercepted touch events in any other system that uses the `Touches` or `EventReader<TouchInput>` resources.
Alternatively, you can manually use the `take_touch_events` binding to get all the recorded touch events. They _must_ be handled and/or dispatched after being
taken, or they will be lost - there is no double buffering or equivalent
\ No newline at end of file
export function orientation_lock(orientation) {
window.screen.orientation.lock(orientation)
}
export function orientation_unlock() {
window.screen.orientation.unlock()
}
export function make_selector_fullscreen(selector) {
const el = document.querySelector(selector);
if (el) {
el.requestFullscreen()
}
}
export function exit_fullscreen() {
document.exitFullscreen()
}
export function is_fullscreen() {
return document.fullscreenEnabled
}
export function is_touch_device() {
return (navigator?.maxTouchPoints ?? 0) > 0
}
export function toggle_selector_fullscreen(selector) {
if (is_fullscreen()) {
exit_fullscreen()
} else {
make_selector_fullscreen(selector)
}
}
export function bind_selector_touch_events(selector) {
function touch(evt) {
let phase = null
switch(evt.type) {
case 'touchstart':
phase = 'Started';
break;
case 'touchend':
phase = 'Ended';
break;
case 'touchmove':
phase = 'Moved';
break;
case 'touchcancel':
phase = 'Cancelled';
break;
}
if (phase == null) {
return
}
for (const touch of evt.changedTouches) {
console.log(touch)
window.touch_events.push({
id: touch.identifier,
phase,
position: [touch.pageX, window.document.documentElement.getBoundingClientRect().height - touch.pageY],
force: null,
})
}
}
if (window.touch_events == null) {
window.touch_events = []
window.touch_handlers = window.touch_handlers || {}
}
const el = document.querySelector(selector)
if (el != null) {
window.touch_handlers[selector] = touch
el.addEventListener('touchstart', touch)
el.addEventListener('touchend', touch)
el.addEventListener('touchcancel', touch)
el.addEventListener('touchmove', touch)
}
}
export function teardown_selector_touch_events(selector) {
const handler = window.touch_handlers?.[selector]
delete window.touch_handlers?.[selector]
if (handler != null) {
const el = document.querySelector(selector)
if (el != null) {
el.removeEventListener('touchstart', handler)
el.removeEventListener('touchend', handler)
el.removeEventListener('touchcancel', handler)
el.removeEventListener('touchmove', handler)
}
}
}
export function take_touch_events() {
if (window.touch_events) {
let events = window.touch_events
window.touch_events = []
return JSON.stringify(events)
}
return '[]'
}
\ No newline at end of file
hard_tabs = true
#group_imports = "StdExternalCrate"
use_field_init_shorthand = true
use_try_shorthand = true
\ No newline at end of file
use bevy_ecs::event::EventWriter;
use bevy_input::touch::{TouchInput, TouchPhase};
use bevy_math::Vec2;
use std::fmt::{Display, Formatter};
#[derive(serde::Deserialize)]
struct InnerTouchInput {
id: u64,
phase: TouchPhase,
position: Vec2,
}
#[derive(Clone, Debug)]
pub struct TouchEventError {
message: String,
}
impl TouchEventError {
pub fn new(message: impl ToString) -> Self {
TouchEventError {
message: message.to_string(),
}
}
}
impl Display for TouchEventError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Touch Error: {}", self.message)
}
}
impl std::error::Error for TouchEventError {}
fn inner_touch_events() -> Result<impl IntoIterator<Item = InnerTouchInput>, TouchEventError> {
let raw_touch_events = super::bindings::take_touch_events();
let touch_events: Vec<InnerTouchInput> =
serde_json::from_str(&raw_touch_events).map_err(|e| TouchEventError::new(e))?;
Ok(touch_events.into_iter())
}
pub fn emit_touch_events(mut events: EventWriter<TouchInput>) {
if let Ok(inner_events) = inner_touch_events() {
for input in inner_events {
events.send(TouchInput {
phase: input.phase,
position: input.position,
id: input.id,
force: None,
});
}
}
}
use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen(module = "/js/bindings.js")]
extern "C" {
pub fn orientation_lock(orientation: String);
pub fn orientation_unlock();
pub fn make_selector_fullscreen(selector: String);
pub fn toggle_selector_fullscreen(selector: String) -> bool;
pub fn exit_fullscreen();
pub fn bind_selector_touch_events(selector: String);
pub fn teardown_selector_touch_events(selector: String);
pub fn take_touch_events() -> String;
pub fn is_fullscreen() -> bool;
pub fn is_touch_device() -> bool;
}
#[cfg(target_arch = "wasm32")]
pub mod bevy;
#[cfg(target_arch = "wasm32")]
pub mod bindings;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment