Last active
February 4, 2023 23:04
-
-
Save jjxtra/36ab49364a90e24c0c0ac5ddde19db4a to your computer and use it in GitHub Desktop.
Don't pay for Cloudflare Pro just to get Mirage, use this script instead to cross fade and lazy load your images. No external js required! https://dailytechnews.io
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// code used on https://dailytechnews.io to fade images | |
// MIT license, no support provided :) | |
// | |
// Setup: | |
// - you must put your img tag inside a span tag and set the img tag css to display: none. Make sure the css for the span and img tag has a height set. Width can be 100%. | |
// - query on all cross-fade spans in a script at the end of your body tag and call DailyTechNewsIoFade(eachSpan) for each one | |
// - i.e. _loop('.cross-fade', DailyTechNewsIoFade); | |
// - required html markup: <span class='cross-fade'><img class='your-image-class' src='/path/lowres/image.webp' title='title' alt='alt' data-src='/path/highres/image.webp' data-dim='1.5' /></span> | |
// - the src is a very low res image (32x32 max dimensions, with full res image aspect ratio as close as possible) | |
// - title and alt are up to you | |
// - data-dim is width/height of full res image | |
// - data-src is full image source | |
// | |
// javascript helpers to avoid using jquery | |
const _win = window; | |
const _doc = document; | |
const _qs = s => _doc.querySelector(s); | |
const _qsa = s => _doc.querySelectorAll(s); | |
const _ga = (o, a) => o.getAttribute(a); // object, attribute name | |
const _sa = (o, a, v) => o.setAttribute(a, v); // object, attribute name, value | |
const _ra = (o, a) => o.removeAttribute(a); // object, attribute name | |
const _ae = (o, e, f) => o.addEventListener(e, f); // object, event name, function | |
const _gi = i => _doc.getElementById(i); // id | |
const _ce = (t, h = '') => { var e = _doc.createElement(t); e.innerHTML = h; return e; } // tag, html | |
const _eu = c => encodeURIComponent(c); // string | |
const _hide = e => { e.style.visibility = 'hidden'; e.style.display = 'none'; } // element | |
const _show = e => { e.style.visibility = 'visible'; e.style.display = e.tagName == 'div' ? 'block' : ''; } // element | |
const _st = setTimeout; // shortcut for setTimeout | |
const _si = setInterval; // shortcut for setInterval | |
// loop through array, or if a is a string, loop through matching selector elements | |
const _loop = (a, f) => { a = typeof a == 'string' ? _qsa(a) : a; for (var i = 0; i < a.length; i++) { f(a[i], i); } } | |
_win.isInViewport = obj => | |
{ | |
var elementTop = obj.offsetTop; | |
var elementBottom = elementTop + obj.offsetHeight; | |
var wh = _win.innerHeight; | |
// total area is one vh up from top of viewport and 2vh down | |
var viewportTop = window.scrollY - wh; | |
var viewportBottom = viewportTop + wh + wh + wh; | |
return elementBottom > viewportTop && elementTop < viewportBottom; | |
}; | |
// img is the span containing your img tag | |
function DailyTechNewsIoFade(img) | |
{ | |
const crossFades = [0, 1000]; // instant and normal fade (ms) | |
var loaded = false; | |
var crossFadeIndex = 0; | |
var oi = img.firstChild; | |
var alt = _ga(oi, 'alt'); | |
var ds = _ga(oi, 'data-src'); | |
// if you want to dynamically change the source based on the screen width or height, you could do so here by changing the ds var | |
var dim = _ga(oi, 'data-dim'); // width/height ratio, needs to be known to keep low res image the right bounds | |
var cl = _ga(oi, 'class'); | |
var ti = _ga(oi, 'title'); | |
var dimFloat = parseFloat(dim); | |
var loading = 'lazy'; | |
var priority = ''; | |
var visibleFold = _win.isInViewport(img); | |
if (visibleFold) | |
{ | |
// no lazy and high priority if in or near fold | |
priority = 'high'; | |
loading = ''; | |
} | |
_ra(oi, 'data-sric'); | |
_ra(oi, 'data-dim'); | |
_ra(oi, 'alt'); | |
// keep the low res image the right dimensions | |
function oiResize() | |
{ | |
var width = img.offsetWidth; | |
var height = img.offsetHeight; | |
var widthAspect = Math.min(width, (height * dimFloat)); | |
widthAspect = Math.round(widthAspect * 2) / 2; | |
widthAspect = widthAspect.toFixed(1); | |
_sa(oi, 'width', widthAspect); | |
_sa(oi, 'height', height); | |
img.style.width = widthAspect; | |
img.style.height = height; | |
}; | |
function oiFade() | |
{ | |
crossFadeIndex = 1; | |
oi.style.filter = 'blur(0.5rem)'; | |
oi.style['mixed-blend-mode'] = 'plus-lighter'; | |
_show(oi); | |
}; | |
oiResize(); | |
_ae(oi, 'resize', oiResize); | |
// create new image tag for the final image | |
var ni = new Image; | |
ni.style['z-index'] = 500; | |
ni.style.opacity = 0; // keep hidden until we load | |
_show(ni); | |
ni.className = cl; | |
_sa(ni, 'title', ti); | |
_sa(ni, 'alt', alt); | |
if (priority.length != 0) | |
{ | |
ni.fetchpriority = priority; | |
} | |
if (loading.length != 0) | |
{ | |
ni.loading = loading; | |
} | |
// image load event | |
function imgLoad() | |
{ | |
if (loaded) | |
{ | |
return; | |
} | |
loaded = true; | |
_ra(ni, 'load'); | |
_ra(ni, 'error'); | |
_ra(ni, 'alt'); | |
_ra(ni, 'title'); | |
_ra(ni, 'data-cfidx'); | |
var animationTime = crossFades[crossFadeIndex]; | |
function animationCompletion() | |
{ | |
oi.remove(); | |
ni.style.opacity = '1'; | |
ni.style.transition = ''; | |
ni.parentElement.style.width = ''; | |
ni.parentElement.style.height = ''; | |
}; | |
if (animationTime <= 100) | |
{ | |
animationCompletion(); | |
} | |
else | |
{ | |
// fade in new final img | |
ni.style.transition = `opacity ${animationTime}ms`; | |
ni.style.opacity = '1'; | |
animationTime *= 1.5; | |
oi.style.transition = `opacity ${animationTime}ms`; | |
oi.style.opacity = '0'; | |
_st(animationCompletion, animationTime); | |
} | |
}; | |
// image error event | |
function imgError() | |
{ | |
// failure, I guess the low res img is all they get :| | |
ni.remove(); | |
}; | |
// set events | |
_ae(ni, 'load', imgLoad); | |
_ae(ni, 'error', imgError); | |
ni.src = ds; | |
img.appendChild(ni); | |
if (ni.complete) | |
{ | |
imgLoad(); | |
} | |
else | |
{ | |
// setup cross fade from previous img tag, if more than 500 milliseconds, we put in the low image and cross fade from it to the full image | |
_st(oiFade, 500); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment