This is the 8th day of my participation in Gwen Challenge

Review the

In the last article, we talked about what a component is and what tools you need to develop a component. After that, we took you through some minor changes to the webPack-template directory structure and configuration files. In this installment, we will introduce components into templates and how to debug and configure our components

For those of you who haven’t read the first article, please click on how to Pack front-end components with WebPack.

The import plug-in

Go back to the SRC/Components/better-draggable-ball/index.ts file we created in the previous article and paste in the plugin code we wrote earlier

// Plug-in code
interfaceDefaultPositionType { x? :number, y? :number
}
interfaceOptions { autoAdsorbent? :boolean; hideOffset? :number; defaultPosition? : DefaultPositionType; }export default class Drag {
  / / element
  element: HTMLElement;

  // Screen size
  screenWidth: number;

  screenHeight: number;

  // Element size
  elementWidth: number;

  elementHeight: number;

  isPhone: boolean;

  // Coordinates of the current element
  elementX: number;

  elementY: number;

  / / element offset
  elementOffsetX: number;

  elementOffsetY: number;

  // Whether to drag
  moving: boolean;

  / / adsorption
  autoAdsorbent: boolean;

  / / hide
  hideOffset: number;

  constructor(element: HTMLElement, dConfig: Options = {}) {
    dConfig = this.InitParams(dConfig);
    this.element = element;
    this.screenWidth = window.innerWidth || window.outerWidth || 0;
    this.screenHeight = window.innerHeight || window.outerHeight || 0;
    this.elementWidth = this.element.offsetWidth || 0;
    this.elementHeight = this.element.offsetHeight || 0;
    this.isPhone = /(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent);
    this.element.style.position = 'absolute';
    this.elementX = 0;
    this.elementY = 0;
    this.elementOffsetX = 0;
    this.elementOffsetY = 0;
    this.moving = false;
    this.autoAdsorbent = dConfig.autoAdsorbent;
    this.hideOffset = this.elementWidth * dConfig.hideOffset;
    if (!this.isPhone) {
      console.error('Warning!! The current plugin version is mobile only.);
    }
    // Default location
    this.setElementPosition(dConfig.defaultPosition.x, dConfig.defaultPosition.y);
    this.watchTouch();
  }

  protected InitParams(dConfig: Options):Options {
    // Handle unconfigured parameters in Options
    return {
      autoAdsorbent: dConfig.autoAdsorbent || false.hideOffset: dConfig.hideOffset || 0.defaultPosition: dConfig.defaultPosition || { x: 0.y: 0}}; }private watchTouch(): void {
    this.element.addEventListener('touchstart'.(event: TouchEvent) = > {
      const rect = (event.target as HTMLElement).getBoundingClientRect();
      // The height of the page to be rolled
      // Not compatible with IE
      const docScrollTop = document.documentElement.scrollTop;
      this.elementOffsetX = event.targetTouches[0].pageX - rect.left;
      this.elementOffsetY = event.targetTouches[0].pageY - rect.top - docScrollTop;
      this.moving = true;
      this.element.addEventListener('touchmove'.this.move.bind(this), { passive: false });
    });
    window.addEventListener('touchend'.() = > {
      this.moving = false;
      document.removeEventListener('touchmove'.this.move);
      if (this.autoAdsorbent) this.adsorbent();
    });
  }

  private setElementPosition(x: number.y: number) :void {
    // Overflow processing
    // Overflow scope
    // But the page is out of the screen, calculate the current screen range
    const leftScope = this.moving ? 0 : 0 - this.hideOffset;
    // Maximum value of current screen right
    const rs = this.screenWidth - this.elementWidth;
    const rightScope = this.moving ? rs : rs + this.hideOffset;
    const bottomScope = this.screenHeight - this.elementHeight;
    if (x <= leftScope && y <= 0) {
      [x, y] = [leftScope, 0];
    } else if (x >= rightScope && y <= 0) {
      [x, y] = [rightScope, 0];
    } else if (x <= leftScope && y >= bottomScope) {
      [x, y] = [leftScope, bottomScope];
    } else if (x >= rightScope && y >= bottomScope) {
      [x, y] = [rightScope, bottomScope];
    } else if (x > rightScope) {
      x = rightScope;
    } else if (y > bottomScope) {
      y = bottomScope;
    } else if (x <= leftScope) {
      x = leftScope;
    } else if (y <= 0) {
      y = 0;
    }
    this.elementX = x;
    this.elementY = y;
    this.element.style.top = `${y}px`;
    this.element.style.left = `${x}px`;
  }

  private move(event: TouchEvent): void {
    event.preventDefault();
    if (!this.moving) return;
    this.elementY = (event.touches[0].pageX - this.elementOffsetX);
    this.elementX = (event.touches[0].pageY - this.elementOffsetY);
    const ex = (event.touches[0].pageX - this.elementOffsetX);
    const ey = (event.touches[0].pageY - this.elementOffsetY);
    this.setElementPosition(ex, ey);
  }

  private animate(targetLeft: number.spd: number) :void {
    const timer = setInterval(() = > {
      let step = (targetLeft - this.elementX) / 10;
      // Round up the step size if it is greater than 0 and round down if it is less than 0.
      step = step > 0 ? Math.ceil(step) : Math.floor(step);
      // Animation principle: Target position = current position + step size
      const x = this.elementX + step;
      this.setElementPosition(x, this.elementY);
      // Check if the slow animation has stopped
      if (Math.abs(targetLeft - this.elementX) <= Math.abs(step)) {
        // Handle decimal assignments
        const xt = targetLeft;
        this.setElementPosition(xt, this.elementY);
        clearInterval(timer);
      }
    }, spd);
  }

  private adsorbent():void {
    // Determine the adsorption direction
    // Screen center point
    const screenCenterY = Math.round(this.screenWidth / 2);
    // left Maximum value
    const rightScope = this.screenWidth - this.elementWidth;
    // Determine the adsorption direction according to the center point
    if (this.elementX < screenCenterY) {
      this.animate(0 - (this.hideOffset), 10);
    } else {
      this.animate(rightScope + (this.hideOffset), 10); }}}Copy the code

Development and debugging

In the process of component development, it is often necessary to debug the function of the component and show the style. At this time, we can use the webpack-dev-server plug-in, which provides us with a basic Web server and has the function of updating the page in real time.

Webpack – dev – server installation

npm install --save-dev webpack-dev-server
Copy the code

! This is just to install it first, and we will configure it for use in the configuration file later

Create a new Drag page and its TS and SCSS files in the Pages folder to debug the component:

The Drag page contents are:

// Drag.html
<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Drag</title>
</head>
<body>
    <h2>
        Drag.html
    </h2>
</body>
</html>
Copy the code
// Drag.scss* {margin: 0;
  padding: 0;
}
body{
  padding: 20px;
}
#drag{
  width: 50px;
  height: 50px;
  background-color: rgb(238.238.238);
  border-radius: 50%;
  border: 5px solid rgb(170.170.170);
}
p{
  height: 50px;
}
Copy the code
//Drag.ts
import './Drag.scss';
import Drag from '.. /.. /components/better-draggable-ball/index';

const dragDom = document.createElement('div');
dragDom.setAttribute('id'.'drag');
const body = document.getElementsByTagName('body') [0];
body.appendChild(dragDom);
new Drag(dragDom, {
  defaultPosition: { x: 10.y: 10 },
  autoAdsorbent: true});Copy the code

Make a copy of the webpack.config.ts file in the root directory of the project and name it webpack.config.dev.ts.

To modify the webpack.config.dev.ts file:

In terms of configuration types, we need to make some changes. Originally we used the config type in the webpack configuration object module, but now we need to use another module (webpack-dev-server) to configure the devServer property in the configuration object. The config in Webpack does not have the attribute of devServer type, so we define a Configuration interface as the type of Configuration file and let it inherit the Config in webpack. When it under devServer WebpackDevServerConfiguration correspond

// webpack.config.dev.ts
import { Configuration as webpackConfiguration } from 'webpack';
import {Configuration as WebpackDevServerConfiguration} from 'webpack-dev-server';

interface Configuration extendswebpackConfiguration{ devServer ? : WebpackDevServerConfiguration; }Copy the code

Add devServer attribute and configure the running directory and service interface. Compress refers to whether the code is GZIP compressed:

const config: Configuration = {
	// Ignore some code
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true.port: 9000,}}Copy the code

Add a Drag page to the Entry and plugins properties

// webpack.config.dev.ts
entry: {
	// Ignore some code
    Drag: './src/pages/Drag/Drag.ts'./ / Drag page
  },
Copy the code
// webpack.config.dev.ts
new HtmlWebpackPlugin({
      title: 'Drag'.filename: 'Drag.html'.template: './src/pages/Drag/Drag.html'.chunks: ['Drag'.'main'],}).Copy the code

Modify package.json file:

The config parameter specifies the configuration file. If not, the default is the webpack.config.ts file

The open parameter automatically opens the target URL when the service is started

"scripts": {
    "... Some commands are ignored here... "
    "serve": "webpack serve --config webpack.dev.config.ts --open"
  },
Copy the code

In order to facilitate everyone CVCVCV method, I directly put the whole webpack.config.dev.ts paste up haha, lazy programmer is the first productivity.

// webpack.config.dev.ts
import * as path from 'path';
import { Configuration as webpackConfiguration } from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import ESLintPlugin from 'eslint-webpack-plugin';
import {Configuration as WebpackDevServerConfiguration} from 'webpack-dev-server';
interface Configuration extendswebpackConfiguration{ devServer ? : WebpackDevServerConfiguration; }const config: Configuration = {
  mode: 'production'.entry: {
    main: './src/main.ts'.index: './src/pages/index/index.ts'./ / the index page
    Drag: './src/pages/Drag/Drag.ts'./ / hello page
    'better-draggable-ball': './src/components/better-draggable-ball/index.ts'./ / better - draggable - ball plug-in
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: (pathData:any) = > (pathData.chunk.name === 'better-draggable-ball' ? 'js/components/[name]/[name].js' : 'js/[name]/[name].js'),
    clean: true,},devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true.port: 9000,},resolve: {
    extensions: ['.tsx'.'.ts'.'.js'],},module: {
    rules: [{test: /\.s[ac]ss$/i,
        use: [
          // Generate JS strings as style nodes
          'style-loader'.// Convert CSS to CommonJS modules
          'css-loader'.// Compile Sass to CSS
          'sass-loader',]}, {test: /\.css$/i,
        use: ['style-loader'.'css-loader'],}, {test: /\.(png|svg|jpg|jpeg|gif)$/i.type: 'asset/resource'}, {test: /\.(woff|woff2|eot|ttf|otf)$/i.type: 'asset/resource'}, {test: /\.tsx? $/,
        use: 'ts-loader'.exclude: /node_modules/,}]},plugins: [
    new HtmlWebpackPlugin({
      title: 'index'.filename: 'index.html'.template: './src/pages/index/index.html'.chunks: ['index'.'main'],}).new HtmlWebpackPlugin({
      title: 'Drag'.filename: 'Drag.html'.template: './src/pages/Drag/Drag.html'.chunks: ['Drag'.'main'],}).new ESLintPlugin({
      extensions: ['js'.'ts'].exclude: '/node_modules/',})]};export default config;
Copy the code

After NPM run serve is executed, webserver will start to run and build a service environment, and the corresponding URL address will also be displayed in terminal. Webserver will also automatically open the browser for us to access the corresponding URL address.

When this is not the Drag page we want, there are now two ways to switch to the Drag page:

  • In the browser to the URL path is modified to http://localhost:8080/Drag.html

    • This is not recommended; you need to manually modify the service every time you start it
  • In the DevServer object, add the openPage property so that the page is displayed automatically

    • devServer: {
      	openPage: 'Drag.html',}Copy the code

Running again, the browser automatically opens the Drag page and our better-drag-Ball component is displayed.

The last

At this point, the debug environment for our component has been deployed.

Thank you for watching. In the next article, we’ll package this component in a multi-version output package that lets users use it directly by referencing javascript files.

😀😀 follow me and don’t get lost! 😀 😀