為Tueri.io構建React圖像優化組件

Let’s face it, image optimization is hard. We want to make it effortless.

面對現實吧,圖像優化非常困難。 我們希望毫不費力。

When we set out to build our React Component there were a few problems we wanted to solve:

當我們開始構建React組件時,我們要解決一些問題:

  • Automatically decide image width for any device based on the parent container.

    根據父容器自動確定任何設備的圖像寬度。

  • Use the best possible image format the user’s browser supports.

    使用用戶瀏覽器支持的最佳圖像格式。

  • Automatic image lazy loading.

    自動圖像延遲加載。

  • Automatic low-quality image placeholders (LQIP).

    自動低質量圖像占位符(LQIP)。

Oh, and it had to be effortless for React Developers to use.

哦,React開發人員必須毫不費力地使用它。

這是我們想出的: (This is what we came up with:)

<Img src={ tueriImageId } alt='Alt Text' />

Easy right? Let’s dive in.

容易吧? 讓我們潛入。

計算圖像尺寸: (Calculating the image size:)

Create a <figure /> element, detect the width and build an image URL:

創建一個<figure />元素,檢測寬度并構建圖像URL:

class Img extends React.Component {constructor(props) {super(props)this.state = {width: 0}this.imgRef = React.createRef()}componentDidMount() {const width = this.imgRef.current.clientWidththis.setState({width})}render() {// Destructure props and stateconst { src, alt, options = {}, ext = 'jpg' } = this.propsconst { width } = this.state// Create an empty query stringlet queryString = ''        // If width is specified, otherwise use auto-detected widthoptions['w'] = options['w'] || width// Loop through option object and build queryStringObject.keys(options).map((option, i) => {return queryString +=  `${i < 1 ? '?' : '&'}${option}=${options[option]}`})return(<figure ref={this.imgRef}>{ // If the container width has been set, display the image else nullwidth > 0 ? (<imgsrc={`https://cdn.tueri.io/${ src }/${ kebabCase(alt) }.${ ext }${ queryString }`}alt={ alt }/>) : null }</figure>)}
}export default Img

This returns the following HTML:

這將返回以下HTML:

<figure><img src="https://cdn.tueri.io/tueriImageId/alt-text.jpg?w=autoCalculatedWidth" alt="Alt Text" />
</figure>

使用最佳圖像格式: (Use the best possible image format:)

Next, we needed to add support for detecting WebP images and having the Tueri service return the image in the WebP format:

接下來,我們需要添加支持以檢測WebP圖像并使Tueri服務以WebP格式返回圖像:

class Img extends React.Component {constructor(props) {// ...this.window = typeof window !== 'undefined' && windowthis.isWebpSupported = this.isWebpSupported.bind(this)}// ...isWebpSupported() {if (!this.window.createImageBitmap) {return false;}return true;}render() {// ...// If a format has not been specified, detect webp support// Set the fm (format) option in the image URLif (!options['fm'] && this.isWebpSupported) {options['fm'] = 'webp'}// ...return (// ...)}
}// ...

This returns the following HTML:

這將返回以下HTML:

<figure><img src="https://cdn.tueri.io/tueriImageId/alt-text.jpg?w=autoCalculatedWidth&fm=webp" alt="Alt Text" />
</figure>

自動圖像延遲加載: (Automatic image lazy loading:)

Now, we need to find out if the <figure /> element is in the viewport, plus we add a little buffer area so the images load just before being scrolled into view.

現在,我們需要確定<figure />元素是否在視口中,此外,我們還要添加一些緩沖區,以便在滾動到視圖之前加載圖像。

class Img extends React.Component {constructor(props) {// ...this.state = {// ...isInViewport: falselqipLoaded: false}// ...this.handleViewport = this.handleViewport.bind(this)}componentDidMount() {// ...this.handleViewport()this.window.addEventListener('scroll', this.handleViewport)}handleViewport() {// Only run if the image has not already been loadedif (this.imgRef.current && !this.state.lqipLoaded) {// Get the viewport heightconst windowHeight = this.window.innerHeight// Get the top position of the <figure /> elementconst imageTopPosition = this.imgRef.current.getBoundingClientRect().top// Multiply the viewport * buffer (default buffer: 1.5)const buffer = typeof this.props.buffer === 'number' && this.props.buffer > 1 && this.props.buffer < 10 ? this.props.buffer : 1.5// If <figure /> is in viewportif (windowHeight * buffer > imageTopPosition) {this.setState({isInViewport: true})}}}// ...componentWillUnmount() {this.window.removeEventListener('scroll', this.handleViewport)}render() {// Destructure props and state// ...const { isInViewport, width } = this.state// ...return (<figure ref={this.imgRef}>{ // If the container width has been set, display the image else nullisInViewport && width > 0 ? (<img onLoad={ () => { this.setState({ lqipLoaded: true }) } }// .../>) : null }</figure>)}
}export default Img

自動低質量圖像占位符(LQIP): (Automatic low-quality image placeholders (LQIP):)

Finally, when an image is in the viewport, we want to load a 1/10 size blurred image, then fade out the placeholder image when the full-size image is loaded:

最后,當圖像在視口中時,我們要加載1/10大小的模糊圖像,然后在加載全尺寸圖像時淡出占位符圖像:

class Img extends React.Component {constructor(props) {// ...this.state = {// ...fullsizeLoaded: false}// ...}// ...render() {// Destructure props and state// ...const { isInViewport, width, fullsizeLoaded } = this.state// ...// Modify the queryString for the LQIP image: replace the width param with a value 1/10 the fullsizeconst lqipQueryString = queryString.replace(`w=${ width }`, `w=${ Math.round(width * 0.1) }`)// Set the default styles. The full size image should be absolutely positioned within the <figure /> elementconst styles = {figure: {position: 'relative',margin: 0},lqip: {width: '100%',filter: 'blur(5px)',opacity: 1,transition: 'all 0.5s ease-in'},fullsize: {position: 'absolute',top: '0px',left: '0px',transition: 'all 0.5s ease-in'}}// When the fullsize image is loaded, fade out the LQIPif (fullsizeLoaded) {styles.lqip.opacity = 0}return(<figurestyle={ styles.figure }// ...>{isInViewport && width > 0 ? (<React.Fragment>{/* Load fullsize image in background */}<img onLoad={ () => { this.setState({ fullsizeLoaded: true }) } }style={ styles.fullsize }src={`https://cdn.tueri.io/${ src }/${ kebabCase(alt) }.${ ext }${ queryString }`}alt={ alt }/>{/* Load LQIP in foreground */}<img onLoad={ () => { this.setState({ lqipLoaded: true }) } }style={ styles.lqip }src={`https://cdn.tueri.io/${ src }/${ kebabCase(alt) }.${ ext }${ lqipQueryString }`} alt={ alt } /></React.Fragment>) : null}            </figure>)}
}// ...

放在一起: (Putting it all together:)

Image optimization made effortless. Just swap out your regular <img /> elements for the Tueri <Img /> and never worry about optimization again.

圖像優化毫不費力。 只需將您的常規<img />元素換成Tueri <Img /> ,再也不用擔心優化。

import React from 'react'
import PropTypes from 'prop-types'
import { TueriContext } from './Provider'
import kebabCase from 'lodash.kebabcase'class Img extends React.Component {constructor(props) {super(props)this.state = {isInViewport: false,width: 0,height: 0,lqipLoaded: false,fullsizeLoaded: false}this.imgRef = React.createRef()this.window = typeof window !== 'undefined' && window this.handleViewport = this.handleViewport.bind(this)       this.isWebpSupported = this.isWebpSupported.bind(this)}componentDidMount() {const width = this.imgRef.current.clientWidththis.setState({width})this.handleViewport()this.window.addEventListener('scroll', this.handleViewport)}handleViewport() {if (this.imgRef.current && !this.state.lqipLoaded) {const windowHeight = this.window.innerHeightconst imageTopPosition = this.imgRef.current.getBoundingClientRect().topconst buffer = typeof this.props.buffer === 'number' && this.props.buffer > 1 && this.props.buffer < 10 ? this.props.buffer : 1.5if (windowHeight * buffer > imageTopPosition) {this.setState({isInViewport: true})}}}isWebpSupported() {if (!this.window.createImageBitmap) {return false;}return true;}componentWillUnmount() {this.window.removeEventListener('scroll', this.handleViewport)}render() {// Destructure props and stateconst { src, alt, options = {}, ext = 'jpg' } = this.propsconst { isInViewport, width, fullsizeLoaded } = this.state// Create an empty query stringlet queryString = ''// If width is specified, otherwise use auto-detected widthoptions['w'] = options['w'] || width// If a format has not been specified, detect webp supportif (!options['fm'] && this.isWebpSupported) {options['fm'] = 'webp'}// Loop through option prop and build queryStringObject.keys(options).map((option, i) => {return queryString +=  `${i < 1 ? '?' : '&'}${option}=${options[option]}`})// Modify the queryString for the LQIP image: replace the width param with a value 1/10 the fullsizeconst lqipQueryString = queryString.replace(`w=${ width }`, `w=${ Math.round(width * 0.1) }`)const styles = {figure: {position: 'relative',margin: 0},lqip: {width: '100%',filter: 'blur(5px)',opacity: 1,transition: 'all 0.5s ease-in'},fullsize: {position: 'absolute',top: '0px',left: '0px',transition: 'all 0.5s ease-in'}}// When the fullsize image is loaded, fade out the LQIPif (fullsizeLoaded) {styles.lqip.opacity = 0}const missingALt = 'ALT TEXT IS REQUIRED'return(// Return the CDN domain from the TueriProvider<TueriContext.Consumer>{({ domain }) => (<figurestyle={ styles.figure }ref={this.imgRef}>{// isInViewport && width > 0 ? (<React.Fragment>{/* Load fullsize image in background */}<img onLoad={ () => { this.setState({ fullsizeLoaded: true }) } }style={ styles.fullsize }src={`${ domain }/${ src }/${ kebabCase(alt || missingALt) }.${ ext }${ queryString }`}alt={ alt || missingALt }/>{/* Load LQIP in foreground */}<img onLoad={ () => { this.setState({ lqipLoaded: true }) } }style={ styles.lqip }src={`${ domain }/${ src }/${ kebabCase(alt || missingALt) }.${ ext }${ lqipQueryString }`} alt={ alt || missingALt } /></React.Fragment>) : null}            </figure>)}</TueriContext.Consumer>)}
}Img.propTypes = {src: PropTypes.string.isRequired,alt: PropTypes.string.isRequired,options: PropTypes.object,ext: PropTypes.string,buffer: PropTypes.number
}export default Img

實際觀看: (See it in action:)

Try out a live demo on CodeSandbox:

在CodeSandbox上進行現場演示:



Originally published at Tueri.io

最初發表于Tueri.io

翻譯自: https://www.freecodecamp.org/news/building-the-react-image-optimization-component-for-tueri-io/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/391739.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/391739.shtml
英文地址,請注明出處:http://en.pswp.cn/news/391739.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

紅黑樹分析

紅黑樹的性質&#xff1a; 性質1&#xff1a;每個節點要么是黑色&#xff0c;要么是紅色。 性質2&#xff1a;根節點是黑色。性質3&#xff1a;每個葉子節點&#xff08;NIL&#xff09;是黑色。性質4&#xff1a;每個紅色節點的兩個子節點一定都是黑色。不能有兩個紅色節點相…

overlay 如何實現跨主機通信?- 每天5分鐘玩轉 Docker 容器技術(52)

上一節我們在 host1 中運行了容器 bbox1&#xff0c;今天將詳細討論 overlay 網絡跨主機通信的原理。 在 host2 中運行容器 bbox2&#xff1a; bbox2 IP 為 10.0.0.3&#xff0c;可以直接 ping bbox1&#xff1a; 可見 overlay 網絡中的容器可以直接通信&#xff0c;同時 docke…

第 132 章 Example

這里介紹一個負載均衡放置問題&#xff0c;我們可以把它擺放在任何位置&#xff0c;每種方案都各有優缺點&#xff0c;需要根據你的實際情況選擇使用 適用于HAProxy / Nginx / LVS 等等 這里用web,db為例子&#xff0c;講述負載均衡之間的關系 132.1. 雙負載均衡的用法 User --…

Python:實現圖片裁剪的兩種方式——Pillow和OpenCV

原文&#xff1a;https://blog.csdn.net/hfutdog/article/details/82351549 在這篇文章里我們聊一下Python實現圖片裁剪的兩種方式&#xff0c;一種利用了Pillow&#xff0c;還有一種利用了OpenCV。兩種方式都需要簡單的幾行代碼&#xff0c;這可能也就是現在Python那么流行的原…

第一個應在JavaScript數組的最后

by Thomas Barrasso由Thomas Barrasso 第一個應在JavaScript數組的最后 (The first shall be last with JavaScript arrays) So the last shall be [0], and the first [length — 1].所以最后一個應該是[0] &#xff0c;第一個[length_1]。 – Adapted from Matthew 20:16–根…

鼠標移動到ul圖片會擺動_我們可以從擺動時序分析中學到的三件事

鼠標移動到ul圖片會擺動An opportunity for a new kind of analysis of Major League Baseball data may be upon us soon. Here’s how we can prepare.不久之后&#xff0c;我們將有機會對美國職棒大聯盟數據進行新的分析。 這是我們準備的方法。 It is tempting to think t…

leetcode 1052. 愛生氣的書店老板(滑動窗口)

今天&#xff0c;書店老板有一家店打算試營業 customers.length 分鐘。每分鐘都有一些顧客&#xff08;customers[i]&#xff09;會進入書店&#xff0c;所有這些顧客都會在那一分鐘結束后離開。 在某些時候&#xff0c;書店老板會生氣。 如果書店老板在第 i 分鐘生氣&#xf…

回到網易后開源APM技術選型與實戰

篇幅一&#xff1a;APM基礎篇\\1、什么是APM?\\APM&#xff0c;全稱&#xff1a;Application Performance Management &#xff0c;目前市面的系統基本都是參考Google的Dapper&#xff08;大規模分布式系統的跟蹤系統&#xff09;來做的&#xff0c;翻譯傳送門《google的Dappe…

持續集成持續部署持續交付_如何開始進行持續集成

持續集成持續部署持續交付Everything you need to know to get started with continuous integration: branching strategies, tests automation, tools and best practices.開始進行持續集成所需的一切&#xff1a;分支策略&#xff0c;測試自動化&#xff0c;工具和最佳實踐。…

51nod 1073約瑟夫環

思路傳送門 &#xff1a;http://blog.csdn.net/kk303/article/details/9629329 n里面挑選m個 可以遞推從n-1里面挑m個 然后n-1里面的x 可以轉換成 n里面的x 的公式 x &#xff08;xm&#xff09;%n; #include <bits/stdc.h> using namespace std;int main () {int n,m;s…

如何選擇優化算法遺傳算法_用遺傳算法優化垃圾收集策略

如何選擇優化算法遺傳算法Genetic Algorithms are a family of optimisation techniques that loosely resemble evolutionary processes in nature. It may be a crude analogy, but if you squint your eyes, Darwin’s Natural Selection does roughly resemble an optimisa…

robot:截圖關鍵字

參考&#xff1a; https://www.cnblogs.com/hong-fithing/p/9656221.html--python https://blog.csdn.net/weixin_43156282/article/details/87350309--robot https://blog.csdn.net/xiongzaiabc/article/details/82912280--截圖指定區域 轉載于:https://www.cnblogs.com/gcgc/…

leetcode 832. 翻轉圖像

給定一個二進制矩陣 A&#xff0c;我們想先水平翻轉圖像&#xff0c;然后反轉圖像并返回結果。 水平翻轉圖片就是將圖片的每一行都進行翻轉&#xff0c;即逆序。例如&#xff0c;水平翻轉 [1, 1, 0] 的結果是 [0, 1, 1]。 反轉圖片的意思是圖片中的 0 全部被 1 替換&#xff…

SVN服務備份操作步驟

SVN服務備份操作步驟1、準備源服務器和目標服務器源服務器&#xff1a;192.168.1.250目標服務器&#xff1a;192.168.1.251 root/rootroot 2、對目標服務器&#xff08;251&#xff09;裝SVN服務器&#xff0c; 腳本如下&#xff1a;yum install subversion 3、創建一個新的倉庫…

SpringCloud入門(一)

1. 系統架構演變概述 #mermaid-svg-F8dvnEDl6rEgSP97 .label{font-family:trebuchet ms, verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-F8dvnEDl6rEgSP97 .label text{fill:#333}#mermaid-svg-F8dvnEDl6rEgSP97 .node rect,#merm…

PullToRefreshListView中嵌套ViewPager滑動沖突的解決

PullToRefreshListView中嵌套ViewPager滑動沖突的解決 最近恰好遇到PullToRefreshListView中需要嵌套ViewPager的情況,ViewPager 作為頭部添加到ListView中&#xff0c;發先ViewPager在滑動過程中流暢性太差幾乎很難左右滑動。在網上也看了很多大神的介紹&#xff0c;看了ViewP…

神經網絡 卷積神經網絡_如何愚弄神經網絡?

神經網絡 卷積神經網絡Imagine you’re in the year 2050 and you’re on your way to work in a self-driving car (probably). Suddenly, you realize your car is cruising at 100KMPH on a busy road after passing through a cross lane and you don’t know why.想象一下…

數據特征分析-分布分析

分布分析用于研究數據的分布特征&#xff0c;常用分析方法&#xff1a; 1、極差 2、頻率分布 3、分組組距及組數 df pd.DataFrame({編碼:[001,002,003,004,005,006,007,008,009,010,011,012,013,014,015],\小區:[A村,B村,C村,D村,E村,A村,B村,C村,D村,E村,A村,B村,C村,D村,E村…

開發工具總結(2)之全面總結Android Studio2.X的填坑指南

前言&#xff1a;好多 Android 開發者都在說Android Studio太坑了&#xff0c;老是出錯&#xff0c;導致開發進度變慢&#xff0c;出錯了又不知道怎么辦&#xff0c;網上去查各種解決方案五花八門&#xff0c;有些可以解決問題&#xff0c;有些就是轉來轉去的寫的很粗糙&#x…

無聊的一天_一人互聯網公司背后的無聊技術

無聊的一天Listen Notes is a podcast search engine and database. The technology behind Listen Notes is actually very very boring. No AI, no deep learning, no blockchain. “Any man who must say I am using AI is not using True AI” :)Listen Notes是一個播客搜索…