UNPKG

gatsby-background-image

Version:

Lazy-loading React background-image component with optional support for the blur-up effect.

334 lines (275 loc) 11.5 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _inheritsLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/inheritsLoose")); var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireDefault(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _BackgroundUtils = _interopRequireDefault(require("./BackgroundUtils")); var _HelperUtils = require("./HelperUtils"); var _ImageUtils = require("./ImageUtils"); var _StyleUtils = require("./StyleUtils"); var _IntersectionObserverUtils = require("./IntersectionObserverUtils"); /** * Main Lazy-loading React background-image component * with optional support for the blur-up effect. */ var BackgroundImage = /*#__PURE__*/ function (_React$Component) { (0, _inheritsLoose2.default)(BackgroundImage, _React$Component); // Needed to prevent handleImageLoaded() firing on gatsby build. function BackgroundImage(props) { var _this; _this = _React$Component.call(this, props) || this; // Default settings for browser without Intersection Observer available. (0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "_isMounted", false); var isVisible = true; var imgLoaded = false; var IOSupported = false; var fadeIn = props.fadeIn; // If this image has already been loaded before then we can assume it's // already in the browser cache so it's cheap to just show directly. var seenBefore = (0, _ImageUtils.inImageCache)(props); // Browser with Intersection Observer available if (!seenBefore && typeof window !== "undefined" && window.IntersectionObserver) { isVisible = false; IOSupported = true; } // Never render image during SSR if (typeof window === "undefined") { isVisible = false; } // Force render for critical images. if (props.critical) { isVisible = true; IOSupported = false; } // Check if a noscript element should be included. var hasNoScript = !(_this.props.critical && !_this.props.fadeIn); _this.state = { isVisible: isVisible, imgLoaded: imgLoaded, IOSupported: IOSupported, fadeIn: fadeIn, hasNoScript: hasNoScript, seenBefore: seenBefore // Preset backgroundStyles (e.g. during SSR or gatsby build). }; _this.backgroundStyles = (0, _StyleUtils.presetBackgroundStyles)((0, _BackgroundUtils.default)(_this.props.className)); // Start with an empty background image. _this.bgImage = ""; _this.handleImageLoaded = _this.handleImageLoaded.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.handleRef = _this.handleRef.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); // "Fake" a reference to an Image loaded via picture element in background. _this.imageRef = (0, _ImageUtils.createPictureRef)(_this.props, _this.handleImageLoaded); return _this; } var _proto = BackgroundImage.prototype; _proto.componentDidMount = function componentDidMount() { this._isMounted = true; // Update background(-*) styles from CSS (e.g. Styled Components). this.backgroundStyles = (0, _StyleUtils.presetBackgroundStyles)((0, _BackgroundUtils.default)(this.props.className)); if (this.state.isVisible && typeof this.props.onStartLoad === "function") { this.props.onStartLoad({ wasCached: (0, _ImageUtils.inImageCache)(this.props) }); } if (this.props.critical) { var img = this.imageRef; if (img && img.complete) { this.handleImageLoaded(); } } }; _proto.componentWillUnmount = function componentWillUnmount() { this._isMounted = false; }; _proto.handleRef = function handleRef(ref) { var _this2 = this; if (this.state.IOSupported && ref) { (0, _IntersectionObserverUtils.listenToIntersections)(ref, function () { var imageInCache = (0, _ImageUtils.inImageCache)(_this2.props); if (!_this2.state.isVisible && typeof _this2.props.onStartLoad === "function") { _this2.props.onStartLoad({ wasCached: imageInCache }); } _this2.setState({ isVisible: true, imgLoaded: imageInCache }); }); } }; _proto.handleImageLoaded = function handleImageLoaded() { if (this._isMounted) { (0, _ImageUtils.activateCacheForImage)(this.props); this.setState({ imgLoaded: true }); if (this.state.seenBefore) { this.setState({ fadeIn: false }); } if (this.props.onLoad) { this.props.onLoad(); } } }; _proto.render = function render() { var _fixOpacity = (0, _StyleUtils.fixOpacity)((0, _HelperUtils.convertProps)(this.props)), title = _fixOpacity.title, id = _fixOpacity.id, alt = _fixOpacity.alt, className = _fixOpacity.className, _fixOpacity$style = _fixOpacity.style, style = _fixOpacity$style === void 0 ? {} : _fixOpacity$style, fluid = _fixOpacity.fluid, fixed = _fixOpacity.fixed, backgroundColor = _fixOpacity.backgroundColor, Tag = _fixOpacity.Tag, children = _fixOpacity.children, _fixOpacity$classId = _fixOpacity.classId, classId = _fixOpacity$classId === void 0 ? !className ? Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 7) : "" : _fixOpacity$classId; var bgColor = typeof backgroundColor === "boolean" ? "lightgray" : typeof backgroundColor !== "undefined" ? backgroundColor : ""; var transitionDelay = this.state.imgLoaded ? "0.5s" : "0.25s"; if (fluid) { var image = fluid; // Set images and visibility according to images available. var newImageSettings = (0, _ImageUtils.switchImageSettings)({ image: image, bgImage: this.bgImage, imageRef: this.imageRef, isVisible: this.state.isVisible }); this.bgImage = newImageSettings.bgImage ? newImageSettings.bgImage : newImageSettings.lastImage; var pseudoStyles = (0, _StyleUtils.createPseudoStyles)((0, _extends2.default)({ classId: classId, className: className, transitionDelay: transitionDelay, bgColor: bgColor, backgroundStyles: this.backgroundStyles, style: style, fadeIn: this.state.fadeIn }, newImageSettings)); return _react.default.createElement(Tag, { className: "" + (className ? className : "") + (classId && "gatsby-background-image-" + classId) + " gatsby-image-wrapper", style: (0, _extends2.default)({ position: "relative", overflow: "hidden", opacity: 0.99 }, style, this.backgroundStyles), id: id, ref: this.handleRef, key: "fluid-" + JSON.stringify(image.srcSet) }, _react.default.createElement("style", { dangerouslySetInnerHTML: { __html: pseudoStyles } }), this.state.hasNoScript && _react.default.createElement("noscript", { dangerouslySetInnerHTML: { __html: (0, _ImageUtils.noscriptImg)((0, _extends2.default)({ alt: alt, title: title }, image)) } }), children); } if (fixed) { var _image = fixed; var divStyle = (0, _extends2.default)({ position: "relative", overflow: "hidden", display: "inline-block", width: _image.width, height: _image.height, opacity: 0.99 }, style); if (style.display === "inherit") { delete divStyle.display; } // Set images and visibility according to images available. var _newImageSettings = (0, _ImageUtils.switchImageSettings)({ image: _image, bgImage: this.bgImage, imageRef: this.imageRef, isVisible: this.state.isVisible }); this.bgImage = _newImageSettings.bgImage ? _newImageSettings.bgImage : _newImageSettings.lastImage; var _pseudoStyles = (0, _StyleUtils.createPseudoStyles)((0, _extends2.default)({ classId: classId, className: className, transitionDelay: transitionDelay, bgColor: bgColor, backgroundStyles: this.backgroundStyles, style: style, fadeIn: this.state.fadeIn }, _newImageSettings)); return _react.default.createElement(Tag, { className: "" + (className ? className : "") + (classId ? " gatsby-background-image-" + classId : "") + " gatsby-image-wrapper", style: (0, _extends2.default)({}, divStyle, this.backgroundStyles), id: id, ref: this.handleRef, key: "fixed-" + JSON.stringify(_image.srcSet) }, _react.default.createElement("style", { dangerouslySetInnerHTML: { __html: _pseudoStyles } }), this.state.hasNoScript && _react.default.createElement("noscript", { dangerouslySetInnerHTML: { __html: (0, _ImageUtils.noscriptImg)((0, _extends2.default)({ alt: alt, title: title, width: _image.width, height: _image.height }, _image)) } }), children); } return null; }; return BackgroundImage; }(_react.default.Component); BackgroundImage.defaultProps = { critical: false, fadeIn: true, alt: "", id: "", Tag: "div" }; var fixedObject = _propTypes.default.shape({ width: _propTypes.default.number.isRequired, height: _propTypes.default.number.isRequired, src: _propTypes.default.string.isRequired, srcSet: _propTypes.default.string.isRequired, base64: _propTypes.default.string, tracedSVG: _propTypes.default.string, srcWebp: _propTypes.default.string, srcSetWebp: _propTypes.default.string }); var fluidObject = _propTypes.default.shape({ aspectRatio: _propTypes.default.number.isRequired, src: _propTypes.default.string.isRequired, srcSet: _propTypes.default.string.isRequired, sizes: _propTypes.default.string.isRequired, base64: _propTypes.default.string, tracedSVG: _propTypes.default.string, srcWebp: _propTypes.default.string, srcSetWebp: _propTypes.default.string }); BackgroundImage.propTypes = { resolutions: fixedObject, sizes: fluidObject, fixed: fixedObject, fluid: fluidObject, fadeIn: _propTypes.default.bool, title: _propTypes.default.string, id: _propTypes.default.string, alt: _propTypes.default.string, className: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.object]), // Support Glamor's css prop. critical: _propTypes.default.bool, style: _propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.array]), // Using PropTypes from RN. backgroundColor: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.bool]), onLoad: _propTypes.default.func, onError: _propTypes.default.func, onStartLoad: _propTypes.default.func, Tag: _propTypes.default.string, classId: _propTypes.default.string }; var _default = BackgroundImage; exports.default = _default;