├── .babelrc
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── config
├── webpack.config.base.js
├── webpack.config.dev.js
└── webpack.config.prod.js
├── example
├── index.html
└── index.js
├── package.json
├── src
├── assets
│ └── loading.png
└── index.tsx
├── tea.yaml
└── tsconfig.json
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "targets": "> 0.25%, not dead"
7 | }
8 | ],
9 | "@babel/preset-react"
10 | ],
11 | "plugins": [
12 | "@babel/plugin-transform-runtime",
13 | "@babel/plugin-transform-modules-commonjs",
14 | [
15 | "@babel/plugin-proposal-decorators",
16 | {
17 | "legacy": true
18 | }
19 | ],
20 | "@babel/plugin-proposal-class-properties",
21 | "@babel/plugin-proposal-object-rest-spread"
22 | ]
23 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | /dist
8 |
9 | # testing
10 | /coverage
11 |
12 | # production
13 | /build
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 |
3 | language: node_js
4 |
5 | node_js:
6 | - 8
7 |
8 | install:
9 | - npm install
10 | - npm install -g codecov
11 |
12 | before_script:
13 | - npm i -g npm
14 |
15 | script:
16 | - codecov
17 |
18 | after_success:
19 | - bash <(curl -s https://codecov.io/bash)
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Jun
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | react-hook-lazy-image 图片懒加载
14 |
15 | 一个应用React Hooks基于IntersectionObserver API实现的图片懒加载组件,具有如下特点:
16 |
17 | - 简单好用,配置灵活
18 | - 兼容主流浏览器
19 | - 使用React Hooks实现
20 | - 使用TypeScript
21 |
22 |
23 |
24 |
25 |
26 | ## 安装
27 | ```jsx
28 | // 使用yarn安装
29 | yarn add react-hook-lazy-image
30 |
31 | // 使用npm安装
32 | npm install react-hook-lazy-image -S
33 | ```
34 |
35 | ## 使用
36 | ```jsx
37 | import React from 'react';
38 | import LazyImage from 'react-hook-lazy-image';
39 |
40 | const list = [ // image src url ...];
41 |
42 | const LazyLoad:React.FC = () => {
43 | const clientHeight = window.innerHeight;
44 |
45 | const style = {height: 300, width: 400, border: '1px solid #000', margin: '10px'};
46 |
47 | return (
48 |
49 | {
50 | list.map((item, i) => (
51 |
52 | ))
53 | }
54 |
55 | )
56 | }
57 |
58 | export default LazyLoad;
59 | ```
60 |
61 |
62 | ## API
63 | | 属性 | 类型 | 是否必填 | 默认值 | 描述 |
64 | | --- | --- | --- | --- | --- |
65 | | src | string | 否 | - | 图片的真实src,不传默认显示占位图 |
66 | | defaultSrc | string | 否 | LazyImage占位图 | 默认渲染的占位图src地址 |
67 | | style | object | 否 | { width: 300, height: 200 } | 图片样式 |
68 | | options | object | 否 | - | 自定义配置,通过配置可以指定图片与特定的父级元素交叉时才去加载真实图片,祥见[IntersectionObserver](https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver/IntersectionObserver) |
69 |
70 |
71 |
72 | ## 最后
73 | 如果觉得好用,请点个star支持一下哈~
74 | 如果在使用过程中有任何问题或者建议,可以在项目中提交issue,或者通过邮件与我取得联系,我会及时处理~
email:lujun5068@gmail.com
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/config/webpack.config.base.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | resolve: {
3 | extensions: ['.tsx', '.ts', '.js', '.jsx']
4 | },
5 | module: {
6 | rules: [
7 | {
8 | test: /\.jsx?$/,
9 | use: 'babel-loader',
10 | exclude: /node_modules/
11 | },
12 | {
13 | test: /\.tsx?$/,
14 | use: 'ts-loader',
15 | exclude: /node_modules/
16 | },
17 | {
18 | test: /\.(jpe?g|png|gif)/,
19 | use: {
20 | loader: 'file-loader',
21 | options: {
22 | name: 'images/[name][hash:8].[ext]'
23 | }
24 | }
25 | }
26 | ]
27 | }
28 | }
--------------------------------------------------------------------------------
/config/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const merge = require('webpack-merge');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const baseConfig = require('./webpack.config.base');
5 |
6 | const devConfig = {
7 | mode: 'development',
8 | entry: './example/index.js',
9 | output: {
10 | path: path.join(__dirname, '../example'),
11 | filename: '[name].js'
12 | },
13 | devtool: 'source-map',
14 | plugins: [
15 | new HtmlWebpackPlugin({
16 | template: './example/index.html'
17 | })
18 | ],
19 | devServer: {
20 | port: 4000,
21 | open: true,
22 | hot: true
23 | }
24 | }
25 |
26 | module.exports = merge(devConfig, baseConfig);
--------------------------------------------------------------------------------
/config/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const merge = require('webpack-merge');
4 | const baseConfig = require('./webpack.config.base');
5 |
6 | const prodConfig = {
7 | mode: 'production',
8 | entry: './src/index.tsx',
9 | output: {
10 | publicPath: '/',
11 | filename: 'index.js',
12 | path: path.resolve(__dirname, '../dist'),
13 | libraryTarget: 'umd',
14 | libraryExport: 'default',
15 | },
16 | externals: {
17 | react: {
18 | root: "React",
19 | commonjs2: "react",
20 | commonjs: "react",
21 | amd: "react"
22 | },
23 | "react-dom": {
24 | root: "ReactDOM",
25 | commonjs2: "react-dom",
26 | commonjs: "react-dom",
27 | amd: "react-dom"
28 | }
29 | },
30 | plugins: [
31 | new webpack.optimize.ModuleConcatenationPlugin(),
32 | ],
33 | }
34 |
35 | module.exports = merge(prodConfig, baseConfig);
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | lazy image
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import LazyImage from '../src/index';
4 |
5 | const list = [
6 | 'https://cdn.imgbin.com/1/6/10/imgbin-arctic-wolf-furry-fandom-carnivora-red-wolf-others-ZHnTC2tmQMPzs37cWQ7tPqp4R.jpg',
7 | 'https://cdn.imgbin.com/11/3/6/imgbin-london-soldier-icon-british-soldiers-two-royal-guards-illustration-DnXRqwwC7iWRUpyfneT3dqUpc.jpg',
8 | 'https://cdn.imgbin.com/17/18/0/imgbin-warrington-council-cheshire-east-london-borough-of-richmond-upon-thames-london-boroughs-others-hJL5XyKYPb4CjHKDUjJ1S9yWV.jpg',
9 | 'https://cdn.imgbin.com/21/6/22/imgbin-fermanagh-and-omagh-london-borough-of-lambeth-county-fermanagh-london-boroughs-others-LCDYu4VaxCS2kxqWHeaW4wgPf.jpg',
10 | 'https://cdn.imgbin.com/1/10/9/imgbin-london-borough-of-southwark-hayes-city-of-westminster-cities-of-london-and-westminster-geography-foreign-candidates-bwQZvxJYgVDGLtsWkkVrHgYY1.jpg',
11 | 'https://cdn.imgbin.com/12/10/17/imgbin-london-borough-of-hammersmith-and-fulham-silhouette-dog-yoga-sySpTkfUATdQ5A4xKzprWBTbG.jpg',
12 | 'https://cdn.imgbin.com/5/4/21/imgbin-london-borough-of-camden-hammersmith-commercial-building-facade-london-bridge-trading-bpv9mKugy0CnbgTak0Va2Wfy9.jpg',
13 | 'https://cdn.imgbin.com/17/2/24/imgbin-london-borough-of-ealing-london-boroughs-west-london-perceval-house-others-Qtw8cdkHXF7JUW9k7mezLELAR.jpg',
14 | 'https://cdn.imgbin.com/4/18/13/imgbin-kingston-upon-thames-royal-borough-of-kensington-and-chelsea-royal-borough-of-greenwich-london-borough-of-ealing-london-borough-of-hounslow-others-MvTGFyw0UDkbw6s6B5bWBTfZc.jpg',
15 | 'https://cdn.imgbin.com/25/22/16/imgbin-kingston-upon-thames-coat-of-arms-surrey-county-council-law-HA2zNQcxjxmMTM0FjHL4ZrsbD.jpg',
16 | 'https://cdn.imgbin.com/2/15/11/imgbin-county-armagh-counties-of-ireland-republic-of-ireland-coat-of-arms-of-ireland-creative-harp-fvtXjkya9NkurELWVaZndG6jL.jpg',
17 | 'https://cdn.imgbin.com/10/9/18/imgbin-wetsuit-yulex-counties-of-ireland-parthenium-argentatum-county-donegal-surfer-AUZFTFWmktMjJPSa05ZRJfcmB.jpg',
18 | 'https://cdn.imgbin.com/22/16/18/imgbin-counties-of-the-kingdom-of-hungary-zalaegerszeg-list-of-regions-of-hungary-town-with-county-rights-hungarian-Lzu3F4V6eSi0KDQWt53ydV9Fp.jpg',
19 | 'https://cdn.imgbin.com/22/9/1/imgbin-fifa-online-3-need-for-speed-edge-mabinogi-vindictus-nexus-the-kingdom-of-the-winds-festivals-cy4Q8uiTF95ihz6ZBjyCN10tZ.jpg',
20 | 'https://cdn.imgbin.com/25/8/24/imgbin-need-for-speed-shift-need-for-speed-most-wanted-shift-2-unleashed-xbox-360-need-for-speed-hot-pursuit-need-for-speed-8RjDqSUsEzgJFskmbVAR6QjWa.jpg',
21 | 'https://cdn.imgbin.com/23/20/11/imgbin-need-for-speed-hot-pursuit-2-need-for-speed-iii-hot-pursuit-need-for-speed-most-wanted-xbox-360-need-for-speed-69VBuefWmKD6c4P2d1hC2Hv9m.jpg',
22 | ]
23 |
24 |
25 |
26 | const LazyLoad = () => {
27 | const clientHeight = window.innerHeight;
28 |
29 | const style = {height: 300, width: 400, border: '1px solid #000', margin: '10px auto'};
30 |
31 | return (
32 |
33 | {
34 | list.map((item, i) => (
35 |
36 | ))
37 | }
38 |
39 | )
40 | }
41 |
42 | render( , document.getElementById('root'));
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-hook-lazy-image",
3 | "version": "1.1.1",
4 | "description": "A lazy loading image component that applies React Hooks based on IntersectionObserver API",
5 | "main": "dist/index.js",
6 | "files": [
7 | "dist"
8 | ],
9 | "scripts": {
10 | "build": "rm -rf dist && webpack --config ./config/webpack.config.prod.js",
11 | "pub": "npm run build && npm publish",
12 | "dev": "webpack-dev-server --config ./config/webpack.config.dev.js",
13 | "report-coverage": "codecov"
14 | },
15 | "repository": "https://github.com/lujun5068/lazy-image",
16 | "keywords": [
17 | "lazy",
18 | "lazy-image",
19 | "lazy-load-image",
20 | "load-image",
21 | "image",
22 | "懒加载",
23 | "图片懒加载"
24 | ],
25 | "author": "lujun_smile",
26 | "license": "MIT",
27 | "dependencies": {
28 | "react": "^16.12.0",
29 | "react-dom": "^16.12.0"
30 | },
31 | "devDependencies": {
32 | "@babel/cli": "^7.7.0",
33 | "@babel/core": "^7.7.2",
34 | "@babel/plugin-proposal-class-properties": "^7.0.0",
35 | "@babel/plugin-proposal-decorators": "^7.0.0",
36 | "@babel/plugin-transform-modules-commonjs": "^7.0.0",
37 | "@babel/plugin-transform-runtime": "^7.0.0",
38 | "@babel/preset-env": "^7.0.0",
39 | "@babel/preset-react": "^7.0.0",
40 | "@babel/runtime": "^7.7.4",
41 | "@types/node": "^12.12.8",
42 | "@types/react": "^16.9.11",
43 | "@types/react-dom": "^16.9.4",
44 | "babel-loader": "^8.0.6",
45 | "file-loader": "^4.2.0",
46 | "html-webpack-plugin": "next",
47 | "ts-loader": "^6.2.1",
48 | "typescript": "^3.7.2",
49 | "webpack": "^4.41.2",
50 | "webpack-cli": "^3.3.10",
51 | "webpack-dev-server": "^3.1.5",
52 | "webpack-merge": "^4.1.4"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/assets/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lujun5068/lazy-image/c885d859337563129fde04f72c958dd99cc3e7de/src/assets/loading.png
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useRef} from 'react';
2 | interface LazyImageProps {
3 | /**
4 | * 图片的真实src,不传默认显示占位图
5 | */
6 | src?: string;
7 |
8 | /**
9 | * 默认的占位图片,可以自己传,默认使用lazyImage的占位图
10 | */
11 | defaultSrc?: string;
12 |
13 | /**
14 | * 图片样式
15 | */
16 | style?: React.CSSProperties;
17 |
18 | /**
19 | * 自定义配置项
20 | */
21 | options?: IntersectionObserver
22 | }
23 |
24 | const defaultUrl = 'data:image/png;base64,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';
25 |
26 | const defaultImgStyle:React.CSSProperties = {
27 | width: 300,
28 | height: 200
29 | }
30 |
31 | const LazyImage:React.FC = ({src= defaultUrl, style=defaultImgStyle, defaultSrc= defaultUrl, options={}}) => {
32 | const imgRef = useRef(null);
33 |
34 | const setDefaultSrc = () => {
35 | imgRef.current.src = defaultSrc;
36 | }
37 |
38 | const proxyImage = () => {
39 | const img = new Image();
40 | img.onload = () => {
41 | imgRef.current.src = src;
42 | }
43 | return {
44 | setSrc() {
45 | img.src = src;
46 | }
47 | }
48 | }
49 |
50 |
51 | useEffect(() => {
52 | setDefaultSrc();
53 |
54 | const observer = new IntersectionObserver((entries) => {
55 | entries.forEach(item => {
56 | if (item.isIntersecting) {
57 | proxyImage().setSrc();
58 | if (src) {
59 | observer.unobserve(item.target);
60 | }
61 | }
62 | })
63 | }, {...options})
64 |
65 | observer.observe(imgRef.current);
66 |
67 | return () => {
68 | observer.unobserve(imgRef.current);
69 | }
70 |
71 | }, [])
72 |
73 | return (
74 |
75 |
![]()
76 |
77 | )
78 | }
79 |
80 | export default LazyImage;
--------------------------------------------------------------------------------
/tea.yaml:
--------------------------------------------------------------------------------
1 | # https://tea.xyz/what-is-this-file
2 | ---
3 | version: 1.0.0
4 | codeOwners:
5 | - '0xee090f2d1Ab50b0115DE74600390e018ee895291'
6 | quorum: 1
7 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | {
3 | "compilerOptions": {
4 | "outDir": "./dist",
5 | "target": "es5",
6 | "jsx": "react",
7 | "lib": ["dom", "esnext"],
8 | "module": "esnext",
9 | "sourceMap": true,
10 | "allowSyntheticDefaultImports": true,
11 | "moduleResolution": "node",
12 | "rootDir": "src",
13 | "forceConsistentCasingInFileNames": true,
14 | "noImplicitReturns": true,
15 | "suppressImplicitAnyIndexErrors": true,
16 | "noUnusedLocals": true,
17 | "experimentalDecorators": true,
18 | "declaration": true
19 | },
20 | "exclude": [
21 | "example",
22 | "node_modules",
23 | "build",
24 | "dist",
25 | "lib",
26 | "tests"
27 | ]
28 | }
29 |
30 |
--------------------------------------------------------------------------------