65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Webpack 3 实现 Slick 懒加载图片网格

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.20/5 (2投票s)

2017 年 12 月 22 日

CPOL

6分钟阅读

viewsIcon

11460

如何以类似Facebook在帖子中显示图片的方式,在灵活的网格中布局和延迟加载图片。选定的图片将打开一个灯箱,可在轮播中预览。图片的alt文本将转换为图片下方的说明文字。

概述

如何以类似Facebook在帖子中显示图片的方式,在灵活的网格中布局和延迟加载图片。选定的图片将打开一个灯箱,可在轮播中预览。图片的alt文本将转换为图片下方的说明文字。

特点

  • 延迟图片加载
  • ES6 转译器
  • JavaScript 源映射
  • Sass CSS 预处理器
  • PostCSS Autoprefixer
  • CSSnano
  • Webpack 3

 

源代码: https://github.com/jimfrenette/uiCookbook/tree/master/photogrid

入门

如果您尚未安装Node.js,那么这是首要任务。请前往Node.js并进行安装。

在您的CLI中导航到项目根目录,例如终端CygwinPowerShell

输入npm init以交互方式创建package.json文件。暂时接受默认选项即可。此文件中的元数据可以根据需要稍后更新。

npm init

现在项目根目录下应该存在一个package.json文件。这将在稍后由npm用于安装模块和运行脚本。

在项目根目录中创建一个index.html文件。在body中添加一个带有.photogrid样式类的无序图片列表。对于延迟图片加载,不要使用src属性,而是将图片路径放在名为data-src的数据属性中。同时在文档head中包含dist/style.css链接,并在body结束标签之前包含dist/app.js链接。

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" media="all" href="dist/style.css">
</head>
<body>
    <ul class="photogrid">
        <li>
            <img data-src="https://images.unsplash.com/reserve/unsplash_528b27288f41f_1.JPG?auto=format&fit=crop&w=2700&q=80&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D" alt="Sea breeze and splashing waves. A photo by @dankapeter on Unsplash"/>
        </li>
        <li>
            <img data-src="https://images.unsplash.com/photo-1494633114655-819eb91fde40?auto=format&fit=crop&w=2550&q=80&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D" alt="Above it All. A photo by @anthonyintraversato on Unsplash" />
        </li>
        <li>
            <img data-src="https://images.unsplash.com/photo-1511125357779-27038c647d9d?auto=format&fit=crop&w=2551&q=80&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D" alt="Found this beauty while being lost in the streets of Cancún, Mexico. A photo by @odiin on Unsplash" />
        </li>
        <li>
            <img data-src="https://images.unsplash.com/photo-1483919283443-8db97e2bcd81?auto=format&fit=crop&w=2550&q=80&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D" alt="Touring NYC. A photo by @freddymarschall on Unsplash" />
        </li>
        <li>
            <img data-src="https://images.unsplash.com/photo-1487357298028-b07e960d15a9?auto=format&fit=crop&w=2550&q=80&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D" alt="Wind turbines, Greece. A photo by @jeisblack on Unsplash" />
        </li>
    </ul>
    <script async src="dist/app.js"></script>
</body>
</html>

使用Emmet内置于VS Code中),您可以通过在第一行输入感叹号然后选择Tab键来创建index.html内容。

图片延迟加载

这是通过David Walsh的简单图片延迟加载和渐入方法实现的。

创建一个名为src的文件夹。在该文件夹中,创建一个js文件夹和一个sass文件夹。

在js文件夹中,创建一个index.js应用程序入口文件和一个lazyimage.js模块文件。

在 sass 文件夹中,创建一个 style.scss 文件,它将用作 Sass 处理的入口点。添加三个 Sass 分片,_base.scss_photogrid.scss_lazyimage.scss。文件名中的下划线表示该文件是 Sass 分片,因此只有在导入时才会被处理。

在 lazyimage.js 模块中,导出此 Lazyimage ES6 类。

lazyimage.js
export default class Lazyimage {
  constructor(options) {

    this.init();
  }

  init() {

    [].forEach.call(document.querySelectorAll('img[data-src]'), function(img) {
      img.setAttribute('src', img.getAttribute('data-src'));
      img.onload = function() {
        img.removeAttribute('data-src');
      };
    });
  }
}

在应用程序入口点导入 lazyimage 模块。

index.js
import Lazyimage from './lazyimage'

new Lazyimage();

将此Sass添加到基本分片中,用于无序列表和图片元素的默认样式。

_base.scss
ul {
  padding: 0;
}

img {
  border-style: none;
  height: auto;
  max-width: 100%;
}

将此Sass添加到photogrid分片中,使用flexbox将photogrid样式应用于图片列表。

_photogrid.scss
ul.photogrid {
  margin: 0.5vw 0.5vw 0.5vw -0.5vw;
  font-size: 0;
  flex-flow: row wrap;
  display: flex;

  li {
    flex: auto;
    width: 200px;
    margin: 0.5vw;
  }
}

将此 Sass 添加到 lazyimage 部分,以便在图片加载完毕且 data-src 属性已移除后,图片淡入。

_lazyimage.scss
img {
  opacity: 1;
  transition: opacity 0.3s;
}

img[data-src] {
  opacity: 0;
}

将三个分片导入到style Sass文件中。

style.scss
@import "base";
@import "photogrid";
@import "lazyimage";

Webpack

安装Webpack。截至本文撰写之时,版本3.10是最新版本。

npm install --save-dev webpack

在项目根目录下创建 webpack.config.js 文件。此应用程序的 CSS 是使用我上个月的帖子中记录的过程构建的,Webpack 3 Sass cssnano Autoprefixer 工作流。建议您阅读该文章以获取更多信息。

webpack.config.js
const path = require('path')
const webpack = require('webpack')
const ExtractTextPlugin = require('extract-text-webpack-plugin')

module.exports = {
context: path.resolve(__dirname, './src'),
  entry: {
    app: './js/index.js',
    css: './sass/style.scss',
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: '[name].js'
  },
  module: {
    rules: [
    {
      test: /\.(css|scss)$/,
      use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: [
          {
            loader: 'css-loader',
            options: {
              minimize: true || {/* CSSNano Options */}
            }
          },
          {
            loader: 'postcss-loader'
          },
          {
            loader: 'sass-loader'
          }
        ]
      })
    },
    {
      test: /\.js$/,
      use: 'babel-loader'
    }
    ]
  },
  plugins: [
    new ExtractTextPlugin('style.css'),
  ],
  devtool: '#eval-source-map'
}

提取文本插件

安装 extract-text-webpack-plugin。此插件用于将 CSS 从包中提取到 style.css 文件中。

npm install --save-dev extract-text-webpack-plugin

自动前缀

Autoprefixer 使用 caniuse.com 数据评估 CSS 并添加或删除供应商前缀,例如 -webkit 和 -moz。

安装 autoprefixer PostCSS 插件。

npm install --save-dev autoprefixer
  • 由于 devDependencies 是使用 --save-dev 选项安装的,所以 package.json 文件将更新,以便可以使用 npm install 重新安装 node 模块。

在 package.json 文件中,添加一个 browserlist 配置,列出 autoprefixer 的最低浏览器支持。

package.json
{
  "name": "photogrid",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "browserslist": [
    "> 2%",
    "last 2 versions",
    "ie > 9"
  ],

  ...
}

在项目根目录中创建一个 PostCSS 配置模块,用于引入 autoprefixer 插件。

postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}

Babel

Babel是一个JavaScript转译器,它将项目的ES6代码转换为ES5 JavaScript。安装Babel、加载器和预设。

npm install --save-dev babel-core babel-loader babel-preset-es2015

在项目根目录中创建一个.babelrc配置文件。

.babelrc
{
  "presets": [
    [
      "es2015",
      {
        "modules": false
      }
    ]
  ]
}

SASS 和 CSS 加载器

安装这些加载器以处理 extract-text-webpack-plugin 的 Sass 和 CSS。

SASS Loader 将 Sass 编译为 CSS,也需要 node-sass。

npm install --save-dev sass-loader node-sass

PostCSS Loader 使用 PostCSS 处理 CSS。

npm install --save-dev postcss-loader

CSS Loader 解析 CSS 中的 import at-rules 和 url 函数。

npm install --save-dev css-loader

Style Loader 在 DOM 中内联 <style></style>

npm install --save-dev style-loader

首次构建

安装 cross-dev,以便在 Windows 和 OS X 等各种平台上运行 NPM 脚本时,更容易设置和使用开发和生产环境的 NODE_ENV 变量。

npm install --save-dev cross-env

package.json 文件中,为 npm-run-script 定义 devbuild 命令。这些命令用于执行开发或生产 webpack 打包过程。

package.json
...

  ],
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack --watch --progress --colors",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  ...
}

在 webpack 配置 JavaScript 的 module.export 之后,添加此代码,用于各自的 dev 和 build npm 脚本,并使用 NODE_ENV 设置。

webpack.config.js
...

if (process.env.NODE_ENV === 'production') {
  module.exports.devtool = '#source-map'
  module.exports.plugins = (module.exports.plugins || []).concat([
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true,
      compress: {
        warnings: false
      }
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    })
  ])
}
  • 请注意,上面代码片段中的省略号 ... 并非实际代码的一部分,它仅表示被跳过且与示例不相关的代码。在遇到其余代码片段中的省略号时,请记住这一点。要查看完整文件,请查看源代码

使用npm-run-scripts别名npm run执行构建

npm run build

您现在应该在项目根目录中有一个新的 dist 文件夹,其中包含 css 和 js 输出。在浏览器中加载网页,查看照片网格是否按预期呈现。

灯箱

src/js 文件夹中创建一个名为 lightbox.js 的新文件,并在文件顶部添加 jQuery 和 slick-carousel 导入语句。

lightbox.js
import $ from 'jquery'
import 'slick-carousel'

使用 npm 并带上 --save 选项安装 jQuerySlick。这会将它们列为 package.json 文件中的 dependencies

npm install --save jquery slick-carousel
  • 请注意,多个包可以通过单个 npm install 命令安装。为了减少输入,可以使用 npm install 别名 npm i。更多信息请参阅 npm-install 文档。

将 Lightbox 类添加到 lightbox.js 模块中。

lightbox.js
...

export default class Lightbox {
    constructor(options) {

        this.settings = $.extend({/* defaults */}, options);

        this.init();
    }

    init() {

        let source = $(this.settings.source);

        if (source.length) {

            source.each((index, el) => {

                this.create(index, el);
            });
        }
    }

    create(index, el) {

        let lightbox = this.settings.name + '__' + index,
            opener = $(el).find(this.settings.opener);

        $('body').append('<div data-lightbox="' + lightbox + '" class="lightbox"><div></div></div>');

        if (this.settings.type === 'slider') {

            $('div[data-lightbox="' + lightbox + '"] > div')
                .append('<div class="lightbox-slider"></div>');

            var slider = $('div[data-lightbox="' + lightbox + '"] .lightbox-slider');

            slider.slick({
                dots: true
            });

            opener.each((index, el) => {
                this.popSlider(lightbox, slider, el);
            });
        }

        // close button
        $('div[data-lightbox="' + lightbox + '"] > div')
            .prepend('<a class="lightbox-close" href="javascript:void(0)">+</a>');

        $('.lightbox-close').on( 'click', function() {
            $('[data-lightbox="' + lightbox + '"]').removeClass('is-open');
        });

        //close on outside click
        window.onclick = function(evt) {
            if (evt.target.dataset.lightbox == lightbox) {
                $('[data-lightbox="' + lightbox + '"]').removeClass('is-open');
            }
        }

        // close on escape
        $(document).keyup(function(evt) {
            if (evt.which === 27) {
                $('[data-lightbox="' + lightbox + '"]').removeClass('is-open');
            }
        });

    }

    popSlider(lightbox, slider, el) {

        let img = $(el).find('img'),
            src = img.prop('src'),
            slide = document.createElement('div'),
            slideImg = document.createElement('img');

        slideImg.src = src;

        slide.appendChild(slideImg);

        if (img.attr('alt')) {
            let caption = document.createElement('p'),
                captionText = document.createTextNode(img.attr('alt'));

            caption.appendChild(captionText);
            slide.appendChild(caption);
        }

        slider.slick('slickAdd', slide);

        img.wrap('<a href="' + src + '"></a>').on( 'click', function(evt) {

            evt.preventDefault();

            $('[data-lightbox="' + lightbox + '"]').addClass('is-open');

            let index = $(this).closest(el).index();

            slider.slick('slickGoTo', index);
        });
    }
}

在应用程序入口点,导入 lightbox 模块并设置目标为 photogrid 的选项。

index.js
...

import Lightbox from './lightbox'

new Lightbox({
    name: 'lightbox',
    source: '.photogrid',
    opener: 'li',
    type: 'slider'
});

src/sass 文件夹中,创建一个名为 _lightbox.scss 的新 Sass 分部文件。

_lightbox.scss
/* overlay (background) */
.lightbox {
  max-height: 0; /* instead of `display: none` so slider can init properly. */

  position: fixed; /* stay in place */
  z-index: 1;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto; /* enable scroll if needed */
  background-color: rgb(0,0,0); /* fallback color */
  background-color: rgba(0,0,0,0.75); /* black w/ opacity */

  > div {
    position: relative;
    background-color: rgb(0,0,0);
    margin: 15% auto; /* 15% from the top and centered */
    padding: 20px;
    color: #fff;
    //width: 100%; /* could be more or less, depending on screen size */

    .slick-prev,
    .slick-next {
      z-index: 10;
    }
    .slick-prev {
      left: -20px;
    }
    .slick-next {
      right: -20px;
    }
    .slick-dots {
      li button:before {
        color: #fff;
      }
    }
  }

  &.is-open {
    max-height: 100%; /* unhide */
  }
}

/* close button */
.lightbox-close {
  position: absolute;
  top: 2px;
  right: 2px;
  text-align: center;
  line-height: 20px;
  font-size: 20px;
  font-weight: bold;
  color: rgba(255,255,255,0.75);
  width: 20px;
  height: 20px;
  transform: rotate(45deg);
  text-decoration: none;
  z-index: 10;

  &:hover {
    color: rgb(255,255,255);
  }
}

将 lightbox 分片、slick 和 slick-theme 导入到 style Sass 文件中。

style.scss
...

@import "lightbox";

/* node_modules */
@import "~slick-carousel/slick/slick.scss";
@import "~slick-carousel/slick/slick-theme.scss";

构建

在再次构建应用程序和 CSS 之前,需要更新 webpack 配置以用于 slick-theme 中的字体 URL 加载。以下是更新后的模块配置。

webpack.config.js
...

  module: {
    rules: [
    {
      test: /\.(css|scss)$/,
      use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: [
          {
            loader: 'css-loader',
            options: {
              minimize: true || {/* CSSNano Options */}
            }
          },
          {
            loader: 'postcss-loader'
          },
          {
            /* for ~slick-carousel/slick/slick-theme.scss */
            loader: 'resolve-url-loader'
          },
          {
            /* for resolve-url-loader:
               source maps must be enabled on any preceding loader */
            loader: 'sass-loader?sourceMap'
          }
        ]
      })
    },
    {
      test: /\.js$/,
      use: 'babel-loader'
    },
    { /* for ~slick-carousel/slick/slick-theme.scss */
      test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
      loader: 'url-loader?limit=30000&name=[name]-[hash].[ext]'
    }
    ]
  }

...

安装 url-loaderresolve-url-loader,以便 webpack 处理 slick-theme 中的相对路径。

npm install --save-dev url-loader resolve-url-loader

运行构建。

npm run build
  • 对于开发,使用 npm run dev 监视更改并在保存更改时增量构建。

就是这样,刷新浏览器并选择一张照片以打开照片预览灯箱和滑块。

© . All rights reserved.