lazyloading swiper.js preloading the next image? - carousel

Is there a way to preload the next or two next images with a swiper.js while lazyloading is active?
The slider contains 100 images which we don’t want to load while the page opens for obvious reasons but we want the first and the two next images to be loaded to have them present within the next slide while the animation runs.
any ideas?

I am looking for the same thing, and looking at the swiperJS documentation, I stumbled upon the API settings for lazyloading.
Looks like maybe loadPrevNext and loadPrevNextAmount might help us out.
loadPrevNext (boolean, default:false)
Set to true to enable lazy loading for the closest slides images (for previous and next slide images)
(I think they actually mean preload next and previous image, if I check other examples and demo's)
loadPrevNextAmount (number, default: 1)
Amount of next/prev slides to preload lazy images in. Can't be less than slidesPerView
So:
const swiper = new Swiper('.swiper-container', {
lazy: {
loadPrevNext: true, // pre-loads the next image to avoid showing a loading placeholder if possible
loadPrevNextAmount: 2 //or, if you wish, preload the next 2 images
},
});

Since version 9, Swiper does not have a lazy loading API and instead leverages browsers' native lazy loading functionality:
Since version 9 Swiper doesn't have specifid lazy loading API, as it relies on native browser lazy loading feature. To use lazy loading, we just need to set loading="lazy" on images and add preloader element
loadPrevNextAmount is gone from the API. One way to achieve similar behavior is to use the slideChange event to force the next N images to load by setting their loading attribute to eager (assuming they were originally set as lazy and each slide element has a single image you want to preload):
const swiper = new Swiper('.swiper', { ... });
const preloadNext = (n) => {
swiper
.slides
.slice(swiper.activeIndex, swiper.activeIndex + n + 1)
.map(slide => slide.querySelector('img'))
.forEach(s => s.setAttribute('loading', 'eager'));
};
// preload the next 2 images immediately
preloadNext(2);
// preload the next 2 images after changing slides
swiper.on('slideChange', () => preloadNext(2));
Another possible optimization is to wait for the page to fully load, which includes the first image in the swiper, and only then preload the following images. This avoids loading the first 3 images at the same time in order to display the first image faster:
let swiper;
const preloadNext = (n) => {
swiper
.slides
.slice(swiper.activeIndex, swiper.activeIndex + n + 1)
.map(slide => slide.querySelector('img'))
.forEach(s => s.setAttribute('loading', 'eager'));
};
document.addEventListener('DOMContentLoaded', () => {
swiper = new Swiper('.swiper', { ... });
swiper.on('slideChange', () => preloadNext(2));
});
window.addEventListener('load', () => {
preloadNext(2);
});

Related

In React Native / Expo, is there any way to save a specific part of an image?

From some research, I've figured out that expo libraries like takePicturesAsync() are able to take a picture and save it to the app's cache. However, the default state for libraries like these is to save the whole image. Is there any way for me to save a specific part of the image (e.g. the 2500 pixels at the center of the screen)?
Thanks!
You can use the onPictureSaved event to grab & manipulate the image.
takePicture = () => {
if (this.camera) {
this.camera.takePictureAsync({ onPictureSaved: this.onPictureSaved });
}
};
onPictureSaved = photo => {
console.log(photo);
}

Vue: get image width + height returns zero on component load

My component is a modal which displays an <img> with some tags on top.
I get the image dimensions using const size = this.$refs.media.getBoundingClientRect().
However, the height and width is 0, so the tags become placed wrong.
So it seems the image hasn't loaded yet? If I delay getting the height+width with setTimeout, I do get the width and height. But it seems like a not too reliable workaround.
I have "v-cloak" on the modal, but that doesn't work.
I can get the html element with this.$refs.media, but it doesn't mean that the image is loaded, apparently.
So, is there a way to check if the image is loaded? Is there some way to delay the component until the image is loaded?
One more thing: if I add a "width" and "height" in inline style on the img, then those values are available directly. But I don't want to set the size that way because it ruins the responsiveness.
The code doesn't say much more, but it is basically:
<modal>
<vue-draggable-resizable // <-- each tag
v-for="( tag, index ) in tags"
:x="convertX(tag.position.x)"
:y="convertY(tag.position.y)"
(other parameters)
convertX (x) {
const size = this.$refs.media.getBoundingClientRect()
return x * (size.width - 100) // <--- 0 width
async mounted () {
this.renderTags(this.media.tags) // <-- needs img width and height
Updated, solution
As the below answer says, I can use the onload event. I also came across the "#load" event, which I tried and it worked. I can't find it in the documentation though(?) (I searched for #load and v-on:load). Using "onload" directly on the image element didn't work though, so #load seems to be the way.
If I have a data property:
data () {
return {
imgLoaded: false
I can set it to true with a method using #load on the image:
<img
#load="whenImgLoaded"
<template v-if="imgLoaded = true">
<vue-draggable-resizable
:x="convertX(tag.position.x)"
:y="convertY(tag.position.y)"
(other parameters)
methods: {
whenImgLoaded(){
this.imgLoaded = true;
this.renderTags(this.media.tags)
}
You could load the image first via javascript, and after it's loaded run the renderTags method.
E.g., do something like this:
async mounted() {
let image = new Image();
image.onload = function () {
this.renderTags(this.media.tags)
};
image.src = 'https://some-image.png'
}

Layout centered on one node

Is there a way to require that a layout doesn't change the position of one "root" node while considering it during the algorithm ? Or equivalently, is there a way to always center the camera to this node/keep the camera at the same relative position to this node ?
A bit of context. I am working on an iteratively built graph. Each time parts are added to the graph, the layout is completed. The graph may grow too big to be printed on a screen, and alternatives to the fit options are welcome. What is important though, is that the user is able to follow the node he selected. The best would be that this node doesn't move.
Here is an outline of the general approach. It can be applied with whatever strategy you like re. zoom and pan. The main thing is that you need to know the start and end positions of each node. So you run the layout in a batch, creating a visual nop, and then run a preset layout with the desired zoom and pan.
const clone = obj => Object.assign({}, obj);
const savePos1 = n => n.scratch('_layoutPos1', clone(n.position()));
const savePos2 = n => n.scratch('_layoutPos2', clone(n.position()));
const restorePos1 = n => n.position(n.scratch('_layoutPos1'));
const getPos2 = n => n.scratch('_layoutPos2');
cy.startBatch();
const nodes = cy.nodes();
const layout = cy.layout(myLayoutOptions); // n.b. animate:false
const layoutstop = layout.promiseOn('layoutstop');
nodes.forEach(savePos1);
layout.run();
await layoutstop;
nodes.forEach(savePos2);
nodes.forEach(restorePos1);
cy.endBatch();
cy.layout({
name: 'preset',
animate: true,
positions: getPos2,
// specify zoom and pan as desired
zoom,
pan
}).run();

framework7 with swiper.js swipe scroll to top not working

I have this function with swiper. I would like know how to use this function to scroll back to the top of the page of the next slide. so far it only scrolls to the top of the next slide when I use onAutoplay callback which I do not want. My slides has different text and images which scrolls that is why I want to return to the top of the next or previous slide when I swipe left or right. Please provide me with an example if possible how to improve my snippet. Thank you.
$$(document).on('pageInit', function (e) {
var mySwiper = myApp.swiper('.swiper-container',{
pagination: '.swiper-pagination',
paginationHide: false,
onSlideChangeEnd: function(swiper) {
$(".page-content").animate({
scrollTop: 0
}, 'slow');
}
});
})
Change your code to this:
var mySwiper = new Swiper('.swiper-container');
mySwiper.on('slideChangeEnd',function(){ alert('sample alert');});
More information about the swiper API:
Swiper API documentation

Howto refresh lightgallery when dynamicEl has changed

I have different albums, the contents of which I want to show in lightgallery.
My initial call goes like :
$('.dlCms_c_Carousel').lightGallery({
dynamic : true ,
dynamicEl : dlThis.LGDynEl ,
thumbnail : true ,
mode : 'lg-fade' ,
});
This works perfect, but then, when opening another album, I want the one or the other way to refresh such that the new content of dynamicEl is shown.
Is there a way to do it ?
I was trying a
$('.dlCms_c_Carousel').data("lightGallery").destroy(true)
before, but that messed up. I.e. there seemed to be some functionality, but it looked like the lightbox lost some styling and wasn't opaque any more.
Any hints ?
I'm using Waypoint.js for infinite scrolling with lightgallery.
The solution that I found for dynamic refresh was:
var gallery = $(".infinite-container").lightGallery({
selector: '.item'
});
var infinite = new Waypoint.Infinite({
element: document.querySelector('.infinite-container'),
onAfterPageLoad: () => {
gallery.data('lightGallery').destroy(true);
gallery = $(".infinite-container").lightGallery({
selector: '.item'
});
}
});
From my experience, the object returned from the lightgallery initialization has to be used on the destroy.
Hope it helps.