写一个简单基于vue的拖拽插件并发布到npm。

目的:

1.任何元素,只需要插入v-drag即可拖动,

2.可根据自定义属性控制结束拖动后是否吸附最近的墙面(屏幕边界)

3.可根据自定义属性来判断pc与移动端,分别执行mouse事件和touch事件

首先声明一个对象,并拥有install方法,然后将其暴露出去。不多解释,官方文档已经很明确了。

const VDrag = {}
VDrag.install = function (Vue) {
}
export default VDrag

Vue参数是被use调用时传入Vue实例,我们的目标是通过自定义指令实现,于是只需要在vue上添加一条自定义指令即可

const VDrag = {}
VDrag.install = function (Vue) {
   Vue.directive('drag', function (el,binding,vnode){

   })
}
export default VDrag

 其中,我们需要通过el来获取元素的一些信息,如元素当前位置等;通过binding获取自定义指令的传值(目前这个插件没用到);通过vnode获取组件上的自定义属性及各种on事件。

let element = el
    element.setAttribute('draggable',false)
    element.style.userDrag = 'none'
    // 正则筛出原本带有的样式,以便之后拼接
    let oldStyle = element.style.cssText.replace(/^(position|left|top):\w+;&/g,'')
    let newStyle
    const onmousemove=(e)=>{
      newStyle = oldStyle + `position:absolute;left:${e.x - element.clientWidth/2 + 'px'};top:${e.y - element.clientHeight/2 + 'px'}`
      element.style.cssText = newStyle
    }
    element.onmousedown=()=>{
      element.addEventListener('mousemove', onmousemove)
    }
    element.onmouseup=(()=>{
      element.removeEventListener('mousemove',onmousemove)
      if(securityObject(vnode.data,'attrs.stick-wall')){
        stickWall(element)
      }
      if(securityObject(vnode.data,'on.dragChange')){
        // vnode.data.on.change(newStyle.match(/(?<=left:).*?(?=;)/g))
        vnode.data.on.dragChange({
          x: element.style.left,
          y: element.style.top
        })
      }

以上代码大概实现了基本的拖拽,并且根据自定义属性stick-wall判断是否贴墙操作,并执行dragChange事件,将xy轴信息传入。

其中stickWall与securityObject方法被我提取成了公用方法,代码如下:

// 吸墙计算
const stickWall = (element) =>{
  // body宽度
  let bodyWidth =  window.innerWidth
  // body高度
  let bodyHeight =  window.innerHeight
  // 拖动元素宽度
  let elWidth = element.clientWidth
  // 拖动元素高度
  let elHeight = element.clientHeight
  // 拖动元素距离左边距离
  let elLeft = parseInt(element.style.left)
  // 拖动元素距离上边距离
  let elTop = parseInt(element.style.top)
  // 距屏幕边的距离,上右下左
  let distance = [
    {
      name:'top',
      value: elTop
    },
    {
      name: 'right',
      value: bodyWidth-elWidth-elLeft
    },
    {
      name: 'bottom',
      value: bodyHeight-elTop-elHeight
    },
    {
      name: 'left',
      value: elLeft
    }
  ]
  // 求出最小值
  let minNumber = Math.min.apply(null,distance.map(item=> item.value))
  // 根据最小值找到对应方向
  let minDirection = distance.find(item => item.value === minNumber).name
  // 各个方向赋值字典
  let directionMap = {
    top: 0,
    right: bodyWidth - elWidth,
    bottom: bodyHeight - elHeight,
    left: 0
  }
  // bottom与right转为top与left
  let transferDirection = {
    top: 'top',
    right: 'left',
    bottom: 'top',
    left: 'left'
  }
  // 元素最终赋值
  element.style[transferDirection[minDirection]] = directionMap[minDirection] + 'px'

}
// 安全获取对象属性
const securityObject = (obj,attr)=>{
  let array = attr.split('.')
  let value = obj
  array.forEach((item)=>{
    if(value){
      value = value[item]
    } else {
      value = undefined
    }
  })
  return value
}

export {stickWall,securityObject}

至此,这个组件算是完成了。只需要引入即可使用了

//main.js

import VDrag from '插件路径'

Vue.use(VDrag)
//template
<div v-drag :stick-wall="true" style="width: 500px;height: 500px;background-color: yellow;position: absolute">
</div>

接下来准备发布插件,首先将代码打包成一个js文件(我这脚手架用的是简化版cli——webpack-simple);更改webpack.config.js

var path = require('path')
var webpack = require('webpack')

module.exports = {
  entry: './src/lib/drag.js', //打包的入口文件
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',    //打包后输出文件夹
    filename: 'vue-drag.js', //打包后文件名
    library: 'VDrag', // library指定的就是你使用require时的模块名,这里便是require("VDrag")
    libraryTarget: 'umd', //libraryTarget会生成不同umd的代码,可以只是commonjs标准的,也可以是指amd标准的,也可以只是通过script标签引入的。
    umdNamedDefine: true // 会对 UMD 的构建过程中的 AMD 模块进行命名。否则就使用匿名的 define。
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ],
      },      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
          }
          // other vue-loader options go here
        }
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[ext]?[hash]'
        }
      }
    ]
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    },
    extensions: ['*', '.js', '.vue', '.json']
  },
  devServer: {
    historyApiFallback: true,
    noInfo: true,
    overlay: true
  },
  performance: {
    hints: false
  },
  devtool: '#eval-source-map'
}

if (process.env.NODE_ENV === 'production') {
  module.exports.devtool = '#source-map'
  // http://vue-loader.vuejs.org/en/workflow/production.html
  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 build,得到打包后的js

接下来修改package.json,主要是设置一些发布信息,如作者信息、版本号、是否私人等,以下关键代码:

"name": "vue-drag-wkq", //不能重名,否无法发布为公有插件
  "description": "基于vue的拖拽指令,只需标签中加入v-drag即可实现拖拽",
  "version": "1.0.116",    // 版本号,每次发布插件时都需要有变化,否则无法发布
  "author": "wangkaiqiang001 <1426422145@qq.com>",
  "license": "MIT",
  "private": false,    // 为false才可以让其他开发者通过npm下载使用
  "main": "dist/vue-drag.js",  //指定被引入时操作的文件(import xxx from 'vue-drag-wkq')
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
  },

然后登陆npm账号:

npm login

输入帐密,登陆;

执行npm publish;

成功(失败一般是名称重复或者版本号忘记改)

然后可以愉快的通过npm install下载自己的的插件了

ps:其中有一点很疑惑,就是打包过后本地引用然后use是不可以的,但是发布到npm在use却可以。按我理解两者这两者应该没差,就算发布到npm,我package.json中main的指向也是该文件。希望知道为什么的朋友可以告诉下是什么原理^_^

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值