lazysizes icon indicating copy to clipboard operation
lazysizes copied to clipboard

Usage of Network information API

Open cxq opened this issue 8 years ago • 4 comments
trafficstars

Hello, A new browser API is available since few time: http://wicg.github.io/netinfo/

We could be able to load a different quality of images depending on the user connection speed. If user is having 3G, we could serve a lighter image and vice-versa.

What do you think?

Thanks, Xiu

cxq avatar Nov 08 '17 07:11 cxq

@cxq

Not 100% sure about this. Because this is a use case of the srcset attribute itself. Which means the browser should handle this case automatically. Maybe file a bug report here: https://crbug.com/wizard

In case you want to try it out, you can use the optimumx plugin and use the following configuration as a start (Please let me know if you come up with a better getOptimumX function).:

(function () {
	'use strict';

	function getNetInfo(){
		var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || {};

		return {
			saveData: connection.saveData || false,
			download: connection.download || 10,
		};
	}

	function getOptimumX(/*element*/){
		var netinfo = getNetInfo();
		var dpr = window.devicePixelRatio || 1;
		var maxDpr = netinfo.saveData ?
			dpr > 1.4 ?
				1.2 :
				dpr - 0.05 :
			2;

		if(netinfo.download < 20 || netinfo.saveData){

			if(dpr > 2.6){
				dpr *= 0.6; // returns 1.8 for 3
			} else if(dpr > 1.9){
				dpr *= 0.8; // returns 1.6 for 2
			} else {
				dpr -= 0.01; // returns 0.99 for 1
			}

			if(netinfo.download < 2 || netinfo.saveData){
				dpr *= dpr > 1.5 ? 0.8 : 0.9;
			} else if(netinfo.download < 5 && dpr > 1.5){
				dpr *= 0.9;
			}
		}


		return Math.min(Math.round(dpr * 100) / 100, maxDpr);
	}

	window.lazySizesConfig = window.lazySizesConfig || {};
	window.lazySizesConfig.getOptimumX = getOptimumX;
	window.lazySizesConfig.constrainPixelDensity = true;
})();

aFarkas avatar Nov 08 '17 12:11 aFarkas

I'm playing with this since there still doesn't seem to be any support from browsers coming anytime soon.

Note: It should be connection.downlink, connection.download does not exist in the spec.

charlespwd avatar Feb 18 '20 19:02 charlespwd

Here's my stab at it:

/* global arguments */
/* eslint no-restricted-modules: ["error", { "patterns": ["*"] }] */
/* eslint no-restricted-imports: ["error", { "patterns": ["*"] }] */
/**
 * This file will be included by lazysizes. It is the first file loaded
 * and must execute really fast. This is why we dissalow imports here.
 **/

const memoizeOnce = fn => {
  let value;
  let lastArgs;
  return () => {
    if (!value || !lastArgs || lastArgs[0] !== arguments[0]) {
      lastArgs = [...arguments];
      value = fn(...lastArgs);
    }
    return value;
  };
};

const EFFECTIVE_TYPES = {
  'slow-2g': 0,
  '2g': 1,
  '3g': 2,
  '4g': 3,
};

// Use this for debugging/testing.
const getItem = k => JSON.parse(localStorage.getItem(k));
const getConnection = () =>
  navigator.connection || navigator.mozConnection || navigator.webkitConnection || {};

export function getNetInfo() {
  const connection = getConnection();
  const localSaveData = getItem('lazySizes.saveData');
  const localEffectiveType = getItem('lazySizes.effectiveType');
  const effectiveTypeId =
    localEffectiveType || // For QA/Testing
    connection.effectiveType || // For browsers that support it (Chrome, FirefoxForAndroid, Lighthouse)
    '4g'; // Other browsers not supported :( fallback to default

  return {
    saveData: localSaveData || connection.saveData || false, // if user asked for less data usage on the user agent.
    effectiveType: EFFECTIVE_TYPES[effectiveTypeId],
  };
}

const getMaxDpr = (saveData, dpr) => {
  if (saveData && dpr > 1.4) {
    return 1.2;
  } else if (saveData) {
    return dpr - 0.05;
  }
  return dpr;
};

function getOptimumX() {
  const netinfo = getNetInfo();
  let dpr = window.devicePixelRatio || 1;
  const ABSOLUTE_MAX_DPR = 2; // bit.ly/retina-capping
  const maxDpr = getMaxDpr(netinfo.saveData, Math.min(dpr, ABSOLUTE_MAX_DPR));

  // OK, I agree this looks like a bunch of magic numbers. I got those from
  // https://github.com/aFarkas/lazysizes/issues/445#issuecomment-342803941
  // The idea is to always have > 1 for higher dpi displays but still bring
  // it down a bit if the user exhibits slow download speed or if the user
  // asked for reduced data usage.
  if (netinfo.effectiveType < EFFECTIVE_TYPES['4g'] || netinfo.saveData) {
    if (dpr > 2.6) {
      dpr *= 0.6; // returns 1.8 for 3
    } else if (dpr > 1.9) {
      dpr *= 0.8; // returns 1.6 for 2
    } else {
      dpr -= 0.01; // returns 0.99 for 1
    }

    // If speed is really slow, kick it down a bit more.
    if (netinfo.effectiveType < EFFECTIVE_TYPES['3g']) {
      dpr *= dpr > 1.5 ? 0.8 : 0.9;
    }
  }

  return Math.min(Math.round(dpr * 100) / 100, maxDpr);
}

export default memoizeOnce(getOptimumX);

charlespwd avatar Feb 24 '20 14:02 charlespwd

This is interesting, but the use of localStorage etc makes thing over-complicated in my opinion, is it really that expensive to retrieve that information once per page load?

@aFarkas I think you meant connection.downlink, because there is no download property.

Also, check it against any number more than 10 can be useless, MDN states that

Note that Chrome-based browsers do not conform to the specification, and arbitrarily cap the reported downlink at a maximum of 10 Mbps as an anti-fingerprinting measure. Similar caps exist for the reported latency.

That said, I think a number greater than 2 is acceptable for a normal website load? Perhaps I'm so used to the terrible internet connection here?

yellow1912 avatar Feb 18 '21 08:02 yellow1912