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 文字溢出,可以通过如下方式解决:
// 通过在 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,则可以直接解决这个问题。