跳到主要内容前端开发常用工具函数与样式技巧合集 | 极客日志JavaScript大前端
前端开发常用工具函数与样式技巧合集
前端开发中常需处理响应式布局、语音播报、手写签名等交互需求。本文整理了 CSS 滚动条重写、Canvas 绘图、本地存储封装及 Element UI 扩展等实用方案,涵盖自动登出、水印添加、引导页及打印等功能,帮助开发者快速复用常见业务逻辑,提升开发效率。
BackendPro1 浏览 前端开发常用工具函数与样式技巧合集
在日常开发中,我们常遇到一些重复性高、但细节繁琐的场景。这里整理了一些经过实战验证的解决方案,涵盖 CSS 样式、Vue 组件交互及基础工具封装,希望能帮大家在项目中少走弯路。
1. CSS 样式优化
响应式断点设置
针对不同设备调整布局是基础工作,建议统一断点规范:
@media only screen and (max-width: 767px) {}
@media only screen and (min-width: 768px) and (max-width: 991px) {}
@media only screen and (min-width: 992px) and (max-width: 1199px) {}
@media only screen and (min-width: 1200px) {}
全局滚动条重写
默认滚动条样式在不同浏览器下差异较大,通过 SCSS 统一美化体验更佳。
scroll.scss
::-webkit-scrollbar {
width: 8px;
height: 8px;
background-color: #f5f5f5;
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background-color: #888;
: ;
}
::-webkit-scrollbar-thumb:hover {
: ;
}
{
: (, , , ) (, , , );
& {
: ;
}
* {
: thin;
}
}
border-radius
4px
background-color
#555
html
scrollbar-color
rgba
201
210
255
0.8
rgba
204
204
204
0.8
:hover
scrollbar-color
#c9d2ff
#a2a2a3
scrollbar-width
2. 语音播报功能
利用 Web Speech API 实现文字转语音,无需额外依赖。
<template>
<button @click="playVoice">播放语音</button>
</template>
<script>
const synth = window.speechSynthesis;
const msg = new SpeechSynthesisUtterance();
export default {
methods: {
playVoice() {
this.handleSpeak('小朋友,你是否有很多问号');
},
handleSpeak(text) {
msg.text = text;
msg.lang = 'zh-CN';
msg.volume = 1;
msg.rate = 1;
msg.pitch = 1;
synth.speak(msg);
},
handleStop(e) {
msg.text = e;
msg.lang = 'zh-CN';
synth.cancel(msg);
}
}
};
</script>
3. 手写签名组件
<template>
<div style="width: 300px; height: 200px; border: 1px solid red">
<canvas ref="canvas" class="jSignature" tabindex="0" @mousedown="onMouseDown"/>
<footer slot="footer" class="dialog-footer">
<el-button type="danger" @click.native.prevent="clearPanel">清空签名</el-button>
<el-button type="primary" @click="confirm">确认签名</el-button>
<el-button @click.native.prevent="clearPanel">取消</el-button>
</footer>
<img class="imgCanvas" :src="imgUrl" />
</div>
</template>
<script>
import { defineComponent, ref, nextTick } from 'vue';
export default defineComponent({
name: 'environAmbitus',
setup() {
const canvas = ref(null);
const imgUrl = ref();
function onMouseDown(e) {
const el = e.target || e.srcElement;
const ctx = el.getContext('2d');
ctx.beginPath();
ctx.moveTo(e.offsetX, e.offsetY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
el.onmousemove = function(e) {
if (e.which === 0) return;
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
};
el.onmouseup = function() {
el.onmousemove = null;
el.onmouseup = null;
};
el.focus();
}
function clearPanel() {
nextTick(() => {
const ctx = canvas.value.getContext('2d');
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height);
});
}
function confirm() {
nextTick(() => {
try {
imgUrl.value = canvas.value.toDataURL();
} catch (e) {
console.warn(e);
}
});
}
return { onMouseDown, confirm, clearPanel, canvas, imgUrl };
}
});
</script>
4. 拖动改变元素宽度
实现类似表格列宽拖拽的效果,核心在于计算偏移量并限制边界。
<template>
<div>
<ul class="box" ref="box">
<li class="left">西瓜</li>
<li class="resize"></li>
<li class="mid">备注</li>
<li class="resize2"></li>
<li class="right">test</li>
</ul>
</div>
</template>
<script>
export default {
mounted() {
this.dragControllerDiv();
},
methods: {
dragControllerDiv() {
const resize = document.getElementsByClassName('resize');
const resize2 = document.getElementsByClassName('resize2');
const left = document.getElementsByClassName('left');
const right = document.getElementsByClassName('right');
const mid = document.getElementsByClassName('mid');
const box = document.getElementsByClassName('box');
for (let i = 0; i < resize.length; i++) {
resize[i].onmousedown = function(e) {
const startX = e.clientX;
resize[i].left = resize[i].offsetLeft;
document.onmousemove = function(e) {
const endX = e.clientX;
const rightW = right[i].offsetWidth;
let moveLen = resize[i].left + (endX - startX);
const maxT = box[i].clientWidth - resize[i].offsetWidth;
if (moveLen < 150) moveLen = 150;
if (moveLen > maxT - rightW - 150) moveLen = maxT - rightW - 150;
resize[i].style.left = moveLen;
for (let j = 0; j < left.length; j++) {
left[j].style.width = moveLen + 'px';
mid[j].style.width = (box[i].clientWidth - moveLen - rightW - 10) + 'px';
}
};
document.onmouseup = function(evt) {
document.onmousemove = null;
document.onmouseup = null;
resize[i].releaseCapture && resize[i].releaseCapture();
};
resize[i].setCapture && resize[i].setCapture();
return false;
};
}
// resize2 逻辑类似,此处省略部分重复代码以保持简洁
}
}
};
</script>
<style scoped>
.box { width: 800px; height: 32px; overflow: hidden; }
.resize, .resize2 { width: 5px; cursor: w-resize; float: left; }
.left { width: calc(30% - 10px); background: skyblue; float: left; }
.mid { width: 35%; background: #f00; float: left; }
.right { width: 35%; background: tomato; float: left; }
</style>
5. 长时间未操作自动退出
结合 localStorage 记录最后操作时间,定时检测超时。
export default {
setItem(key, value) {
window.localStorage.setItem(key, JSON.stringify(value));
},
getItem(key, defaultValue) {
let value = window.localStorage.getItem(key);
try { value = JSON.parse(value); } catch {}
return value || defaultValue;
},
removeItem(key) { window.localStorage.removeItem(key); },
clear() { window.localStorage.clear(); }
};
import storage from '@/utils/storage';
import router from '@/router';
import { MessageBox } from 'element-ui';
let lastTime = new Date().getTime();
const timeOut = 1 * 60 * 60 * 1000;
let showDialog = false;
window.onload = function() {
window.document.onmousedown = function() {
storage.setItem('lastTime', new Date().getTime());
};
};
function checkTimeout() {
const currentTime = new Date().getTime();
lastTime = storage.getItem('lastTime');
if (currentTime - lastTime > timeOut) {
if (router.currentRoute.name === 'Start') return;
if (!showDialog) {
showDialog = true;
MessageBox.alert('登录超时', '提示', {
type: 'warning',
showClose: false,
duration: 5000,
callback: () => {
showDialog = false;
storage.clear();
router.push({ name: 'Start' });
}
});
}
}
}
export default function() {
window.setInterval(checkTimeout, 30000);
}
6. iframe 自适应高度
监听窗口变化动态调整 iframe 高度,避免滚动条冲突。
<iframe src="http://www.example.com" id="myiframe" scrolling="no" frameborder="0"></iframe>
<script>
function changeFrameHeight(){
var ifm = document.getElementById("myiframe");
ifm.height = document.documentElement.clientHeight;
}
window.onresize = function(){ changeFrameHeight(); }
</script>
7. 浏览器环境检测
let Sys = {};
let ua = navigator.userAgent.toLowerCase();
let s;
(s = ua.match(/msie ([\d.]+)/)) ? Sys.ie = s[1] :
(s = ua.match(/firefox\/([\d.]+)/)) ? Sys.firefox = s[1] :
(s = ua.match(/chrome\/([\d.]+)/)) ? Sys.chrome = s[1] :
(s = ua.match(/opera.([\d.]+)/)) ? Sys.opera = s[1] :
(s = ua.match(/version\/([\d.]+).*safari/)) ? Sys.safari = s[1] : 0;
if (Sys.ie) alert('IE: ' + Sys.ie);
if (Sys.firefox) alert('Firefox: ' + Sys.firefox);
if (Sys.chrome) alert('Chrome: ' + Sys.chrome);
8. 文字展开与收起
<template>
<div class="content">
<div v-for="(al, index) in als" :key="index">
<div class="text more">占位</div>
<div class="text">
<div :class="{'retract': al.status}" :style="{'max-height': al.status ? textHeight : ''}">
{{ al.byrw }}
</div>
<div class="btn">
<p v-if="al.status" @click="al.status = false">展开</p>
<p v-if="!al.status" @click="al.status = true">收起</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return { als: [], textHeight: null };
},
mounted() {
this.$nextTick(() => this.calculateText());
},
methods: {
calculateText() {
const oneHeight = this.$refs.more.scrollHeight;
const twoHeight = oneHeight * 2 || 40;
this.textHeight = `${twoHeight}px`;
const txtDom = this.$refs.textContainer;
for (let i = 0; i < txtDom.length; i++) {
const curHeight = txtDom[i].offsetHeight;
if (curHeight > twoHeight) {
this.$set(this.als, i, Object.assign({}, this.als[i], { status: true }));
} else {
this.$set(this.als, i, Object.assign({}, this.als[i], { status: null }));
}
}
}
}
};
</script>
<style>
.retract:after { content: '...'; position: absolute; bottom: 0; right: 0; }
.btn { cursor: pointer; color: #FFAD41; }
</style>
9. PDF 打印
使用 print-js 库实现 HTML 内容直接打印为 PDF。
<template>
<div id="app">
<div id="divPrint" class="mainbox print">
<el-table :data="tableData" border style="width: 100%">
<el-table-column prop="date" label="日期"></el-table-column>
<el-table-column prop="name" label="姓名"></el-table-column>
<el-table-column prop="address" label="地址"></el-table-column>
</el-table>
</div>
<el-button @click="print">PDF 打印</el-button>
</div>
</template>
<script>
import printJS from 'print-js';
export default {
methods: {
print() {
printJS({
printable: 'divPrint',
type: 'html',
targetStyles: ['*'],
style: '@page {margin:0 10mm};',
ignoreElements: ['no-print']
});
}
}
};
</script>
10. 引导页
<template>
<div>
<el-button type="primary" @click="driverD">引导</el-button>
</div>
</template>
<script>
import { driver } from 'driver.js';
import 'driver.js/dist/driver.css';
const driverObj = driver({
showProgress: true,
steps: [
{ element: '#first', popover: { title: '第一步', description: '介绍功能 A' } },
{ element: '#second', popover: { title: '第二步', description: '介绍功能 B' } }
],
allowClose: false,
keyboardControl: false
});
export default {
mounted() {
if (!localStorage.Bj) {
driverObj.drive();
localStorage.setItem('Bj', 'true');
}
},
methods: {
driverD() { driverObj.drive(); }
}
};
</script>
11. 回到顶部组件
封装 BackTop 组件,监听滚动事件控制显隐。
<template>
<div ref="totop" class="totop" @click="scrollToTop" title="返回顶部">
<svg viewBox="0 0 1024 1024"><path d="M512 225..." fill="#ffffff"/></svg>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
const totop = ref();
const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
};
onMounted(() => {
window.onscroll = function() {
const high = document.documentElement.scrollTop || document.body.scrollTop;
totop.value.style.display = high >= 500 ? 'block' : 'none';
};
});
</script>
<style scoped>
.totop { display: none; position: fixed; bottom: 120px; right: 80px; }
</style>
12. 锚点平滑滚动
<template>
<div class="nav">
<a href="#Java" @click="goAnchor('#Java')">Java</a>
<a href="#Python" @click="goAnchor('#Python')">Python</a>
</div>
<div id="Java" class="item">...</div>
</template>
<script>
export default {
methods: {
goAnchor(selector) {
this.$el.querySelector(selector).scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
}
};
</script>
13. 省市区选择器
基于 Element UI 级联选择器,配合地区数据插件。
npm install element-china-area-data -S
<template>
<el-cascader size="large" :options="options" v-model="selectedOptions" />
</template>
<script>
import { regionData } from 'element-china-area-data';
export default {
data() {
return { options: regionData, selectedOptions: [] };
}
};
</script>
14. 本地存储封装
封装带过期时间的 LocalStorage 和 SessionStorage 方法。
export const localSet = (key, value, day) => {
const d = new Date();
const time = d.setHours(d.getHours() + (24 * day));
window.localStorage.setItem(key, JSON.stringify({ value, time }));
};
export const localGet = (key) => {
const obj = window.localStorage.getItem(key);
if (obj) {
const data = JSON.parse(obj);
if (new Date().getTime() > data.time) {
localStorage.removeItem(key);
return null;
}
return data.value;
}
return '';
};
15. 拖拽 Dialog 指令
自定义 Vue 指令实现 Element UI Dialog 的拖拽功能。
directive/el-drag-dialog/drag.js
export default {
bind(el, binding, vnode) {
const dialogHeaderEl = el.querySelector('.el-dialog__header');
const dragDom = el.querySelector('.el-dialog');
dialogHeaderEl.style.cssText += ';cursor:move;';
dialogHeaderEl.onmousedown = (e) => {
const disX = e.clientX - dialogHeaderEl.offsetLeft;
const disY = e.clientY - dialogHeaderEl.offsetTop;
document.onmousemove = function(e) {
let left = e.clientX - disX;
let top = e.clientY - disY;
dragDom.style.cssText += `;left:${left}px;top:${top}px;`;
};
document.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
};
};
}
};
import elDragDialog from '@/directive/el-drag-dialog/index';
Vue.directive('el-drag-dialog', elDragDialog);
以上这些片段覆盖了日常开发中高频出现的场景,建议收藏备用。实际使用时请根据项目实际情况调整参数和样式。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online