// Copyright (c) 2018 Dominic Masters // // MIT License // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import React from 'react'; import LoadableImage from './LoadableImage'; const DPI_RATIOS = [1,2,4]; export default (props) => { let newProps = {...props}; //Local Scope props let { loadable, image, src, alt, width, height, sources, onLoad, onError, maxWidth, maxHeight, images } = props; //Delete bad props [ "loadable", "image", "src", "alt", "width", "height", "sources", "onLoad", "onError", "maxWidth", "maxHeight", "images" ].forEach(e => delete newProps[e]); width = parseInt(width) || undefined; maxWidth = parseInt(maxWidth) || undefined; height = parseInt(height) || undefined; maxHeight = parseInt(maxHeight) || undefined; if(loadable) return <LoadableImage {...props} />; //Has image prop? Image prop may be either an array of sources of a image. if(image) { if(Array.isArray(image)) { sources = sources || image; } else { src = src || image; } } if(src) { if(src.images) sources = sources || src.images; if(src.width) width = width || src.width; if(src.height) height = height || src.height; } //Image sources = sources || []; //Sort the sources by their size sources.sort((l,r) => { return (l.size||l.width) - (r.size||r.width); }); let sourcesByWidth = {}; let sourceElements = []; let defaultSrc = src; let defaultAlt = alt; let defaultWidth = width; let defaultHeight = height; //Iterate over supplied sources for(let i = 0; i < sources.length; i++) { let source = sources[i]; let width = source.size || source.width; //Thanks to maxWidth prop we need to check if this iteration is the last. let maxWidthBreak = false; if(maxWidth && i > 0) { if(width >= maxWidth) maxWidthBreak = true; } DPI_RATIOS.forEach(ratio => { let scaledWidth = Math.round(width / ratio); //Don't scale less than the smallest image (i.e. 200px@1x shouldn't be 50@4x) if(scaledWidth < (sources[0].size||sources[0].width)) return false; let o = {...source}; o.scale = ratio;//Inject scale (DPI ratio) //Create an array for this screen width let widthKey = `${scaledWidth}`; sourcesByWidth[widthKey] = sourcesByWidth[widthKey] || []; sourcesByWidth[widthKey].push(o);//Add this source }); //Was this the last iteration? if(maxWidthBreak) break; } //Sort by size in ascending order let keys = Object.keys(sourcesByWidth); keys.sort((l, r) => { return parseInt(l) - parseInt(r); }); //let breakNext = false; for(let i = 0; i < keys.length; i++) { let k = keys[i];//The pixel size let ss = sourcesByWidth[k];//Sources at this pixel resolution (array of sources) if(!ss.length) continue; let mediaQuery;//Media query let sss = []; //Try and make this media query be max width by the next key if(i+1 < keys.length) { let nextKey = keys[i+1]; mediaQuery = `(max-width:${nextKey}px)`; } else { mediaQuery = `(min-width:${k}px)`; } for(let x = 0; x < ss.length; x++) { let scale = ss[x]; let source = scale.src || scale.path; sss.push( source + (scale.scale && scale.scale!=1 ? " "+scale.scale+"x" : "") ); } sourceElements.push(<source media={mediaQuery} srcSet={sss.join(", ")} key={i} />); } return ( <picture> { sourceElements } <img {...newProps} onLoad={ onLoad } onError={ onError } src={ defaultSrc } alt={ defaultAlt } width={ defaultWidth } height={ defaultHeight } /> </picture> ); }