Closed squarfed closed 4 years ago
I have a similar need which I had some direction on in #1169. I wanted to take his advice and look at adding support to get metadata(exif) for image.
I did this the other day:
fn rendered(&mut self, first_render: bool) {
// ...
// Initialize spritesheet
let link = self.link.clone();
let onload = Closure::wrap(Box::new(move || {
link.send_message(Msg::SpritesLoaded);
}) as Box<dyn Fn()>);
self.spritesheet
.set_onload(Some(onload.as_ref().unchecked_ref()));
self.spritesheet.set_src("sprites.png");
onload.forget();
In this case, self.spritesheet
was a HtmlImageElement
created outside of view
, and not attached to the DOM. And I didn't come up with the code myself, and cannot really say if it's safe or sensible
Thanks for adding that snippet @Stigjb, that's a decent workaround for now!
Let's track work on supporting onload
here: https://github.com/yewstack/yew/issues/1232
Feel free to re-open this issue if #1232 doesn't fully cover the question
Did you manage to have it working @squarfed? I was able to figure it out for one image, but can't seem to find a way to do it with a collection of images. Consider the following snippet:
let image_loaded = use_state(|| false);
let collections = collections.iter().map(|collection| {
let image_loaded = image_loaded.clone();
let onload = {
let image_loaded = image_loaded.clone();
Callback::from(move |_| {
image_loaded.set(true);
})
};
html! {
<div>
<img src={collection.collection_image_url.clone()} onload={onload.clone()}/>
if {!*image_loaded} {
<div class="loading-overlay">
<div class="loading-animation"></div>
</div>
}
</div>
}
}).collect::<Html>();
This snippet will stop showing the loading-overlay
for all images once the first image is loaded.
And I cannot use the hooks inside the map
function. So, a bit stuck here, any input is greatly appreciated!
A quick update here.
I managed to make it work with web_sys
help and with no hooks at all
use web_sys::{Element, Node};
...
let collections = collections.iter().map(|collection| {
let div: Element = web_sys::window().expect("Can't find window").document().expect("Can't find document").create_element("div").unwrap();
div.set_attribute("class", "loading-overlay").unwrap();
div.set_inner_html("<div class='spinner'></div>");
let node: Node = div.clone().into();
html! {
<div>
<img src={collection.collection_image_url.clone()}
onload={move |_| div.set_attribute("style", "opacity: 0").unwrap()}/>
{ Html::VRef(node) }
</div>
}
}).collect::<Html>();
and following CSS:
.spinner {
width: 60px;
height: 60px;
border: 6px solid white;
border-top: 6px solid #212529;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-overlay {
position: absolute;
top: 0;
width: 250px;
height: 250px;
background-color: rgba(255, 255, 255, 0.8);
display: flex;
align-items: center;
justify-content: center;
opacity: 1;
transition: opacity 0.3s ease;
}
well, it is kinda ugly, but it works!
I would like to implement some form of progressive loading for images, and I need to react to
load
events that signals when an image has been fully downloaded. Any hints how to do that?