日常总结

385 阅读5分钟

JS

Element.getBoundingClientRect()

Element.getBoundingClientRect() 返回元素的大小及其相对于视口的位置。

window.getComputedStyle

返回一个对象包含所有受支持的CSS属性名称的活动值

mousedown/mouseup和click事件的冲突解决

通过时间差来解决鼠标 mousedown/mouseup和click事件的冲突 (利用事件发生时间来判断点击事件时间长短)

var key = false; //设置了一个标志 false为点击事件 ture为鼠标移动事件
var firstTime = 0;
var lastTime = 0;

div.onclick = function() {
    if(key){
        console.log('click');
        key = false;
    }
}

div.onmousedown = function() {
    console.log('mouseDown');
    firstTime = new Date().getTime();    
}

div.onmouseup = function() {
    console.log('mouseUp'); //鼠标抬起后 记录时间 超过200ms就是移动事件    
    lastTime = new Date().getTime();
    if( (lastTime - firstTime < 200){
        key = true;
    } 
}

实现在特定的区域中元素跟随鼠标移动

<body>
    <div class="boxArea">
        <div id="box"></div>
    </div>
    
    <script>
    var addDiv = document.getElementById("box")
    var boxArea = document.getElementsByClassName(boxArea)
    addDiv.addEventListener("mousedown",function(e) {
        e.stopPropagation();
        var x = e.pageX - this.offsetLeft;  // 鼠标的初始位置坐标
        var y = e.pageY - this.offsetTop;

        document.addEventListener("mousemove", move, false)

        function move(e){

            var curX =  e.pageX - x
            var curY =  e.pageY - y

            // 特定区域限制
            var maxLeft = boxArea.offsetWidth-addDiv.offsetWidth;
            var maxTop = boxArea.offsetHeight-addDiv.offsetHeight;

            if(curX <= 0) {
                addDiv.style.left = 0
            }else if(curX>=maxLeft){
                addDiv.style.left = maxLeft + 'px';
            }else{
                addDiv.style.left = curX + 'px';
            }

            if(curY <= 0){
                addDiv.style.top = 0;
            }else if(curY>=maxTop){
                addDiv.style.top = maxTop + 'px';
            }else{
                addDiv.style.top = curY + 'px';
            }

            // 无限制区域时
            // addDiv.style.left = e.pageX - curX + 'px';
            // addDiv.style.top = e.pageY - curY + 'px';
        }

        document.addEventListener("mouseup", function(e){
           document.removeEventListener("mousemove", move)
        }, false)
    }, false)
    </script>
</body>

CSS

按住鼠标移动时, 禁止选中内容区

.content{
    user-select: none|auto|text|contain|all; 
    /*firefox浏览器*/
    -moz-user-select: none|text|all; 
    /*safari、chrome浏览器*/
    -webkit-user-select: none|text|all; /*Safari中不支持该属性值,只能使用none或者text,或者是在html的标签属性中使用*/ 
    /*ie浏览器*/
    -ms-user-select: none|text|element;
}

/*默认不选中*/
.content{
    user-select: none; 
    -moz-user-select: none; 
    -webkit-user-select: none; 
    -ms-user-select: none;
}

属性值:
none : 元素和子元素的文本将无法被选中
text : 文本可以被选中
auto : 文本将根据浏览器的默认属性进行选择
all : 当所有内容作为一个整体时可以被选择。如果双击或者在上下文上点击子元素,那么被选择的部分将是以该子元素向上回溯的最高祖先元素
contain、element : 可以选择文本,但选择范围受元素边界的约束,也就是选择的文本将包含在该元素的范围内。只支持Internet Explorer。

Canvas

canvas图片绘制示例

<body>
    <div class="box">        
        <canvas id="myCanvas" width="600px" height="300px" style="border: 1px solid red;"></canvas>        
    </div>

    <script>
        //获取Canvas对象(画布)   
        var canvas = document.getElementById("myCanvas");   
        //简单地检测当前浏览器是否支持Canvas对象,以免在一些不支持html5的浏览器中提示语法错误   
        if(canvas.getContext){     
            //获取对应的CanvasRenderingContext2D对象(画笔)   
            var ctx = canvas.getContext("2d");         
            //创建新的图片对象   
            var img = new Image();   
            //指定图片的URL   
            img.src = "./images/1.png";   
            //浏览器加载图片完毕后再绘制图片   
            img.onload = function(){   
                //以Canvas画布上的坐标(10,10)为起始点,绘制图像   
                //图像的宽度和高度分别缩放到350px和100px   
                ctx.drawImage(img, 0, 0, 602, 302);  
                //设置字体样式   
                ctx.font = "30px Courier New";   
                //设置字体填充颜色   
                ctx.fillStyle = "blue";   
                //从坐标点(50,50)开始绘制文字   
                ctx.fillText("CodePlaye测试", 350, 250); 
            }; 
        }   
    </script>
</body>

Echarts

Echarts基本应用

以在vue-cli3.x项目中使用为例:

安装
npm i echarts -S

使用
import echarts from "echarts"
Vue.prototype.$echarts = echarts    // 在main.js中配置
// 初始化图表
initEchart(ele, option) {
    let echart = this.$echarts.init(document.getElementById(ele));
    echart.setOption(option);
    window.addEventListener("resize", () => { echart.resize() });       
},

// 初始化配置数据
initOptionData() {
    var that = this;        

    this.option = {

      // 1-提示框组件可以设置在多种地方
        tooltip: {
            trigger: 'axis',   // 触发类型 axis | item | none
            axisPointer: {  // 坐标轴指示器配置项
              type: 'shadow'
            },
            textStyle: {
            }
        },

        // 2-图例组件:标记、文字、颜色
          legend: {
            orient: 'vertical', // 图例列表的布局朝向 horizontal | vertical
          },

        // 3-直角坐标系内绘图网格
        grid: {
            left: '3%',
            right: '4%',
            bottom: '3%',
            containLabel: true // grid区域是否包含坐标轴的刻度标签
        },

        // 4-x轴坐标相关设置
        xAxis: [
            {
                type: 'category',
                data: ['断路器', '隔离开关', '变压器', '避雷器', '母线', '电容器'],
                axisTick: { // 坐标轴刻度相关设置
                    alignWithLabel: true
                },
                axisLine: { // 坐标轴轴线相关设置                   
                    lineStyle: {
                        color: '#999'
                    }
                }
            }
        ],

        // 5-y轴坐标相关设置
        yAxis: [
            {
                type: 'value',
                minInterval: 1,
                name: '(数量)',
                nameTextStyle: {
                    color: '#999'
                },
                axisLine: {
                    show: false,                        
                }
            }
        ],

        // 6-不同类型图例配置
        series: [
            {
                name: '数量',
                type: 'bar',
                data: [10, 52, 200, 334, 390, 330],
                itemStyle: {
                    color: '#b6cffa'
                },
                barWidth: "20%"
            }
        ]
    }
}

饼图 label 超出边界,label被遮挡

修改前 修改后

原因分析 出现这种情况一般是因为在给定盒范围内,饼图太偏上导致的

代码解决

// 通过设置 center 值,使饼图下移一些
options = {
    // ……
    series : [
      {
          type: 'pie',  
          // radius 表示饼图的半径,数组的第一项是内半径,第二项是外半径。
          radius: [20, 110],
          // center 表示饼图的中心(圆心)坐标,数组的第一项是横坐标,第二项是纵坐标。
          // 支持设置成百分比,设置成百分比时第一项是相对于容器宽度,第二项是相对于容器高度。
          center: ['50%', '56%']       
      }
    ]  
}

拓展:

  • 有时会出现 label 文字溢出,可以通过如下方式解决:

image.png

// 通过在 label 中同时设置 position、alignTo、margin 的值
options = {
    // ……
    series : [
      {
          type: 'pie', 
          label:{
             show:true,   
             position:'outer',
             alignTo:'edge',
             margin:0
          },
      }
    ]  
}
  • 直角坐标系内绘图网格的位置调整,通过grid
options = {
   // ……
   grid: {
    right: "1%",
    top: 20
  },
}
// 单个 grid 内最多可以放置上下两个 X 轴,左右两个 Y 轴。

地图 API 相关

百度地图开发

<template>
    <div id="mapContainer"></div>
</template>

// 初始化地图
initMap () {
    this.mapContainer = new BMap.Map('mapContainer', { enableMapClick: false }) // 创建Map实例      
    this.mapContainer.centerAndZoom(new BMap.Point(116.404, 39.915), 15) // 初始化地图,设置中心点坐标和地图级别
    this.mapContainer.enableScrollWheelZoom(true) // 开启鼠标滚轮缩放
    
    // 地图模式的转换
    // this.mapContainer.setMapType(BMAP_NORMAL_MAP) // 默认普通模式
    // this.mapContainer.setMapType(BMAP_SATELLITE_MAP)  // 卫星地图
    
    // 地图常用事件
    this.mapContainer.addEventListener('tilesloaded', function () {
        console.log("---地图加载完成---")
    });
    
    this.mapContainer.addEventListener('click', function(e) {
        console.log("---地图点击---")
        // 点击位置的经纬度
        let {lng, lat} = e.point
    })
    
},

// 创建带有文字标注的点标记
// point 经纬度
markerFn (point) {
    var marker = new BMap.Marker(point, {
        icon: aIcon,  // 自定义图标
        enableDragging: true,  // 设置可拖拽
    })  
    marker.setRotation(90)             // 设置点旋转
    map.addOverlay(marker)             // 将标注添加到地图中

    // offset 相对于点标记的偏移量
    var label = new BMap.Label("我是文字标注",{offset:new BMap.Size(20,-10)})
    marker.setLabel(label)   
},

// 绘制某一区域的边界线(包含如省->市\市->区等)
createLineFn () {
    this.getBoundary('江苏')// 城市区域范围
    let cityArr = ['南京市', '扬州市', '苏州市', '无锡市', '宜兴市']  
    cityArr.map(item => {
       this.getBoundary(item)
    })
},
/**
 * @desc 获取某一地区下的市/县
 * @param province 指定地区
*/
getBoundary (province) {
    var that = this
    var bdary = new BMap.Boundary()
    bdary.get(province, function (rs) {       //获取行政区域
        var count = rs.boundaries.length  //行政区域的点有多少个
        console.log("rs.boundaries==>", rs.boundaries)
        if (count === 0) {
            /*alert('未能获取当前输入行政区域');*/
            return
        }
        var pointArray = []
        for (var i = 0; i < count; i++) {
            var ply = new BMap.Polyline(rs.boundaries[i], { strokeColor: "#cc0000", strokeWeight: 3, strokeOpacity: 1 }) //建立多边形覆盖物
            map.addOverlay(ply)  //添加覆盖物
            pointArray = pointArray.concat(ply.getPath())
        }
        if (province === '江苏') {
            map.setViewport(pointArray) // 调整视野
        }    
    })
},

// 数据清除
clean () {
    // 单个清除覆盖物
    this.mapContainer.removeOverlay(marker)
    
    // 清除地图上的所有覆盖物
    this.mapContainer.clearOverlays()
    // 对于自己定义的一些覆盖物,通过上面方法可能无法清除,此时就需要逐一进行清除  
    let allOverlayList = this.mapContainer.getOverlays();
      //循坏所有点
     for (var i = 0; i < allOverlayList.length; i++) {
          this.mapContainer.removeOverlay(allOverlayList[i]);
     }
}

VUE

vue插件

  • transition-group 如果使用v-for获取的所有元素,使用transition-group实现过渡效果。

  • Vue.Draggable 一款基于Sortable.js实现的vue拖拽插件

  • VuePhotoZoomPro(vue-photo-zoom-pro) 图片放大镜效果

vue中动态绑定class 类名的方法

<!-- 对象形式(多个使用逗号分隔) -->
<p :class="{'p1' : true}"></p>

<!-- 数组形式 -->
<p :class="['p1', 'p2']"></p>   
<p :class="[1<2?'px':'p2']"></p>  

<!-- 数组对象 -->
<p :class="['p1', {'p2': false}]"></p>   

<!-- 方法名(在methods中定义并return) -->
<p :class="setClass"></p>   
methods: {
    setclass () {
        return 'p1'
    }
}

vue项目中 proxy 服务端代理

proxy 就是利用了 Node 反向代理,,原理是因为服务器端没有跨域的概念。

参考理解:浏览器是禁止跨域的,但是服务端不禁止,在本地运行npm run dev等命令时实际上是用node运行了一个服务器,因此proxyTable实际上是将请求发给自己的服务器,再由服务器转发给后台服务器,做了一层代理,因为不会出现跨域问题。

module.exports = {
    devServer: {
        port: 9090,
        proxy: {
          '/api/olis_xz/warning': {
            target: 'http://176.168.0.52:1011/api/olis_xz/warning', 
            secure: true, 
            changeOrigin: true,
            pathRewrite: {
              '^/api/olis_xz/warning': ''
            }
          },
          
          '/api': {
            target: 'http://192.168.0.1:200/',   // 要代理的域名
            changeOrigin: true, // 跨域
            secure: true
            pathRewrite: {
                '^/api': '' // 重写
            }
          },          
        }
    },/* devServer--END*/
}

使用时代码

// /api/getMenu相当于 http://192.168.0.1:200/getMenu
// /api相当于http://192.168.0.1:200

this.$http.get("/api/getMenu", {}).then(res => {
    // ……
}).catch(function(error) {
   // ……
});


/**
你要 http://192.168.0.1:200/api/test 可以在本地调 localhost:8080/api/test,如axios.get('/api/test')
实际上:
localhost:8080/api/test -> http://192.168.0.1:200/api/test


当你调接口后端的命名没有统一给接口前加 /api 这类的标识,那么你可以自己加,也就是你可以在本地调
localhost:8080/api/test,如axios.get('/api/test'),而你要的目标接口是 
http://192.168.0.1:200/test,你就可以用 pathRewrite,遇到 /api 就去找代理 http://192.168.0.1:200 
并且把 /api 重写为 /,如:
localhost:8080/api/test -> http://192.168.0.1:200/test


概括来说,以上面代码设置的为例,会把请求中所有带有/api字段的都替换掉,例如api/getMenu/api,前后两个都会被替换,
导致404等错误,在代理数量比较多的时候容易出现这个问题。
*/
devServer: { proxy: 'http://e.dxy.net' }

这种写法对所有的接口都代理,不止是检测到 `/api` 的接口,比如:
`localhost:8080/api/test` ==> `http://e.dxy.net/api/test`
`localhost:8080/test` ==> `http://e.dxy.net/test`

回车搜索第一次触发,出现刷新页面问题

@keyup.native.enter 第一次触发时,会刷新页面
原因:

  • 事件包裹在form表单中,enter事件默认触发了表单的提交,导致页面刷新;
  • W3C标准中有如下规定:当一个 form 元素中只有一个输入框时,在该输入框中按下回车应提交该表单。如果希望阻止这一默认行为,可以在标签上添加 @submit.native.prevent。 解决:在el-form标签中加上 @submit.native.prevent,阻止表单的默认行为
<el-form :model="cardForm" @submit.native.prevent inline label-width="112px" label-position="top">
    <el-form-item label="卡号:">
        <el-input v-model="cardForm.cardNo" placeholder="请输入" clearable @keyup.enter.native="handleSearch"></el-input>
    </el-form-item>
</el-form>

或者通过在组件中添加一个隐藏的input,多个输入框就不会进行页面刷新问题

<el-input v-show="false"></el-input>

或者通过在input元素上添加默认阻止表单提交的事件

onkeypress="if(event.keyCode == 13) return false;"
<el-input v-model="searchVal" clearable placeholder="请输入" @keypress="handleKeypress($event)" @keyup.enter.native="handleQuery"></el-input>

vue项目中--导出json文件

前端请求接口返回一堆json数据,如何导出json文件并下载

getJson () {
  // 点击下载
  commonApi.downJson().then(res => {
    var data = JSON.stringify(res)
    // encodeURIComponent 解决中文乱码
    let uri = 'data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(data)
    // 通过创建 a 标签来实现下载
    let link = document.createElement('a')
    link.href = uri
    // 对下载的文件命名
    link.download = '数据表.json'
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
  })
},

HTTP

更新后列表数据没更新

调用更新数据的列表uFn后,又调用获取数据列表sFn的接口,如果页面中数据仍然没有更新,则考虑是前端异步接口的执行顺序问题,一般会要求只有在uFn成功后才调用列表接口sFn,则可以直接解决这个问题。