Bài đăng nổi bật

Hướng dẫn thay đổi thư mục root mặc định của Docker trên Linux

Hoàn cảnh: người viết gặp một trường hợp như này Được team hạ tầng cấp cho một máy chủ gồm 2 phân vùng lưu trữ, 1 phân vùng 20GB được gắn và...

How to cache image to memory in Javascript?

Ok, let me show my case:
I have a request that they need to apply parallax (like a anime action) to their website with about 20 images. What they want is loading all images and displaying to their customers.
But loading all images is a huge problem and if applying parallax, each frame must be reloaded that means every time user scrolls mouse we must reload image from server.
How do I solve this problem?
Let start with this script. This script will do:

  • Load images from server.
  • Apply ScrollMagic

<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js" integrity="sha256-lPE3wjN2a7ABWHbGz7+MKBJaykyzqCbU96BJWjio86U=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/ScrollMagic.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/plugins/animation.gsap.min.js" integrity="sha256-+9YNuItWuR4sbqeaNiJOxG0BvptYz4fbUXbIZoH5Jwo=" crossorigin="anonymous"></script>
</head>
<body>
<form class="move">
<fieldset>
<legend>Toggle duration</legend>
<div>
<input type="radio" name="duration" id="bound" value="300" checked="">
<label for="bound">bound to scrollbar (duration = 300)</label>
</div>
<div>
<input type="radio" name="duration" id="unbound" value="0">
<label for="unbound">unbound from scrollbar (duration = 0)</label>
</div>
</fieldset>
</form>
<div class="spacer s2"></div>
<div class="spacer s0" id="trigger"></div>
<div id="imagesequence">
<img id="myimg" src="assets/1.jpg"><br>
<a href="#" class="viewsource">view source</a>
</div>
<div class="spacer s2"></div>
<script>
// define images
var images = [
"assets/2.jpg",
"assets/3.jpg",
"assets/4.jpg",
"assets/5.jpg",
"assets/6.jpg",
"assets/7.jpg",
"assets/8.jpg",
"assets/9.jpg",
"assets/10.jpg",
"assets/11.jpg",
"assets/12.jpg",
"assets/13.jpg",
"assets/14.jpg",
"assets/15.jpg",
"assets/16.jpg",
"assets/17.jpg",
];
// TweenMax can tween any property of any object. We use this object to cycle through the array
var obj = {curImg: 0};
// create tween
var tween = TweenMax.to(obj, 0.5,
{
curImg: images.length - 1,  // animate propery curImg to number of images
roundProps: "curImg",               // only integers so it can be used as an array index
repeat: 3,                                  // repeat 3 times
immediateRender: true,          // load first image automatically
ease: Linear.easeNone,          // show every image the same ammount of time
onUpdate: function () {
console.log(obj.curImg);
$("#myimg").attr("src", images[obj.curImg]); // set the image source
}
}
);
// init controller
var controller = new ScrollMagic.Controller();
// build scene
var scene = new ScrollMagic.Scene({triggerElement: "#trigger", duration: 1000})
.setTween(tween)
// .addIndicators() // add indicators (requires plugin)
.addTo(controller);
// handle form change
$("form.move input[name=duration]:radio").change(function () {
scene.duration($(this).val());
});
</script>
</body>
</html>


If you turn on developer mode of your browser, you will see that each time you scroll your mouse, your browser will reload image from server event you've loaded it before. It's not good with a slow network.To solve this problem, I'll preload all images and cache it to browser's memory with this script:

function preloadImgs(url, callback){
var xhr = new XMLHttpRequest();
xhr.onload = function(){
var reader = new FileReader();
reader.onloadend = function(){
callback(reader.result);
};
reader.readAsDataURL(xhr.response);
};
xhr.open("GET", url);
xhr.responseType = "blob";
xhr.send();
}

And apply it by:

var imageCache = [];
var obj = null;
var tween = null;
var controller = null;
var scene = null;
var lock = 0;
for(var i = 0; i < images.length; i++){
preloadImgs(images[i], function(dataUrl){
imageCache.push(dataUrl);
if(imageCache.length === 16 && lock === 0){
obj = {curImg: 0};
tween = TweenMax.to(obj, 0.5,
{
curImg: imageCache.length - 1,
roundProps: "curImg",
repeat: 1,
immediateRender: true,
ease: Linear.easeNone,
onUpdate: function(){
console.log(obj.curImg);
$("#myImg").attr("src", imageCache[obj.curImg]);
}
});
controller = new ScrollMagic.Controller();
scene = new ScrollMagic.Scene({
triggerElement: "#trigger",
duration: 1000
})
.setTween(tween)
// .addIndicators()
.addTo(controller);
lock = 1;
}
});
}

You'll see that I've changed from directly using images array to Parallax library, I've used imageCache. In imageCache array, I've saved images in base64 format (You can see it in here)
Remember:

  • Because of using memory to cache images, you should decrease your images' size. In my example, if each image's size is 1MB, you must use at least 17MB memory to save.

  • When you begin access this page, you must wait until all images are loaded.
  • I try to use lock to know when all images are loaded.

Không có nhận xét nào:

Đăng nhận xét