mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Screenshots in wasm (#8455)
# Objective - Enable taking a screenshot in wasm - Followup on #7163 ## Solution - Create a blob from the image data, generate a url to that blob, add an `a` element to the document linking to that url, click on that element, then revoke the url - This will automatically trigger a download of the screenshot file in the browser
This commit is contained in:
parent
670f3f0dce
commit
cb286e5b60
4 changed files with 71 additions and 4 deletions
|
@ -1968,7 +1968,7 @@ path = "examples/window/screenshot.rs"
|
|||
name = "Screenshot"
|
||||
description = "Shows how to save screenshots to disk"
|
||||
category = "Window"
|
||||
wasm = false
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "transparent_window"
|
||||
|
|
|
@ -82,3 +82,16 @@ encase = { version = "0.5", features = ["glam"] }
|
|||
# For wgpu profiling using tracing. Use `RUST_LOG=info` to also capture the wgpu spans.
|
||||
profiling = { version = "1", features = ["profile-with-tracing"], optional = true }
|
||||
async-channel = "1.8"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
js-sys = "0.3"
|
||||
web-sys = { version = "0.3", features = [
|
||||
'Blob',
|
||||
'Document',
|
||||
'Element',
|
||||
'HtmlElement',
|
||||
'Node',
|
||||
'Url',
|
||||
'Window',
|
||||
] }
|
||||
wasm-bindgen = "0.2"
|
||||
|
|
|
@ -71,10 +71,47 @@ impl ScreenshotManager {
|
|||
// discard the alpha channel which stores brightness values when HDR is enabled to make sure
|
||||
// the screenshot looks right
|
||||
let img = dyn_img.to_rgb8();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
match img.save_with_format(&path, format) {
|
||||
Ok(_) => info!("Screenshot saved to {}", path.display()),
|
||||
Err(e) => error!("Cannot save screenshot, IO error: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
match (|| {
|
||||
use image::EncodableLayout;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
|
||||
let mut image_buffer = std::io::Cursor::new(Vec::new());
|
||||
img.write_to(&mut image_buffer, format)
|
||||
.map_err(|e| JsValue::from_str(&format!("{e}")))?;
|
||||
// SAFETY: `image_buffer` only exist in this closure, and is not used after this line
|
||||
let parts = js_sys::Array::of1(&unsafe {
|
||||
js_sys::Uint8Array::view(image_buffer.into_inner().as_bytes())
|
||||
.into()
|
||||
});
|
||||
let blob = web_sys::Blob::new_with_u8_array_sequence(&parts)?;
|
||||
let url = web_sys::Url::create_object_url_with_blob(&blob)?;
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let link = document.create_element("a")?;
|
||||
link.set_attribute("href", &url)?;
|
||||
link.set_attribute(
|
||||
"download",
|
||||
path.file_name()
|
||||
.and_then(|filename| filename.to_str())
|
||||
.ok_or_else(|| JsValue::from_str("Invalid filename"))?,
|
||||
)?;
|
||||
let html_element = link.dyn_into::<web_sys::HtmlElement>()?;
|
||||
html_element.click();
|
||||
web_sys::Url::revoke_object_url(&url)?;
|
||||
Ok::<(), JsValue>(())
|
||||
})() {
|
||||
Ok(_) => info!("Screenshot saved to {}", path.display()),
|
||||
Err(e) => error!("Cannot save screenshot, error: {e:?}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
Err(e) => error!("Cannot save screenshot, requested format not recognized: {e}"),
|
||||
},
|
||||
|
|
|
@ -8,17 +8,17 @@ fn main() {
|
|||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, screenshot_on_f12)
|
||||
.add_systems(Update, screenshot_on_spacebar)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn screenshot_on_f12(
|
||||
fn screenshot_on_spacebar(
|
||||
input: Res<Input<KeyCode>>,
|
||||
main_window: Query<Entity, With<PrimaryWindow>>,
|
||||
mut screenshot_manager: ResMut<ScreenshotManager>,
|
||||
mut counter: Local<u32>,
|
||||
) {
|
||||
if input.just_pressed(KeyCode::F12) {
|
||||
if input.just_pressed(KeyCode::Space) {
|
||||
let path = format!("./screenshot-{}.png", *counter);
|
||||
*counter += 1;
|
||||
screenshot_manager
|
||||
|
@ -61,4 +61,21 @@ fn setup(
|
|||
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
..default()
|
||||
});
|
||||
|
||||
commands.spawn(
|
||||
TextBundle::from_section(
|
||||
"Press <spacebar> to save a screenshot to disk",
|
||||
TextStyle {
|
||||
font_size: 25.0,
|
||||
color: Color::WHITE,
|
||||
..default()
|
||||
},
|
||||
)
|
||||
.with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(10.0),
|
||||
left: Val::Px(10.0),
|
||||
..default()
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue