跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaScript大前端

前端开发常用工具函数与样式技巧合集

前端开发中常需处理响应式布局、语音播报、手写签名等交互需求。本文整理了 CSS 滚动条重写、Canvas 绘图、本地存储封装及 Element UI 扩展等实用方案,涵盖自动登出、水印添加、引导页及打印等功能,帮助开发者快速复用常见业务逻辑,提升开发效率。

BackendPro发布于 2025/1/15更新于 2026/9/1058 浏览
前端开发常用工具函数与样式技巧合集

前端开发常用工具函数与样式技巧合集

在日常开发中,我们常遇到一些重复性高、但细节繁琐的场景。这里整理了一些经过实战验证的解决方案,涵盖 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

/* Chrome/Safari */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
  background-color: #f5f5f5;
  border-radius: 4px;
}
::-webkit-scrollbar-thumb {
  background-color: #888;
  border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
  background-color: #555;
}

/* Firefox */
html {
  scrollbar-color: rgba(201, 210, 255, 0.8) rgba(204, 204, 204, 0.8);
  &:hover {
    scrollbar-color: #c9d2ff #a2a2a3;
  }
  * {
    scrollbar-width: thin;
  }
}

在入口文件引入即可生效:

import './scroll.scss'

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. 手写签名组件

基于 Canvas 实现,支持清空和导出图片。

<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 记录最后操作时间,定时检测超时。

utils/storage.js

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(); }
};

utils/astrict.js

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; // 30 分钟
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. 引导页

使用 driver.js 实现新功能引导蒙层。

<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 方法。

// utils/localStorage.js
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;
      };
    };
  }
};

main.js

import elDragDialog from '@/directive/el-drag-dialog/index';
Vue.directive('el-drag-dialog', elDragDialog);

以上这些片段覆盖了日常开发中高频出现的场景,建议收藏备用。实际使用时请根据项目实际情况调整参数和样式。

目录

  1. 前端开发常用工具函数与样式技巧合集
  2. 1. CSS 样式优化
  3. 响应式断点设置
  4. 全局滚动条重写
  5. 2. 语音播报功能
  6. 3. 手写签名组件
  7. 4. 拖动改变元素宽度
  8. 5. 长时间未操作自动退出
  9. 6. iframe 自适应高度
  10. 7. 浏览器环境检测
  11. 8. 文字展开与收起
  12. 9. PDF 打印
  13. 10. 引导页
  14. 11. 回到顶部组件
  15. 12. 锚点平滑滚动
  16. 13. 省市区选择器
  17. 14. 本地存储封装
  18. 15. 拖拽 Dialog 指令

更多推荐文章

查看全部
  • 为什么 Python 成了开发 AI 的主流语言?
  • 基于 Neo4j 与 py2neo 的知识图谱搭建实战
  • Java RESTful 接口开发实战指南
  • AI 原生应用用户意图理解:7 种主流算法对比与选型
  • Milvus 实战:Attu 可视化安装与 Python 整合指南
  • WebSocket 与 MQTT 即时通讯协议对比与架构选型指南
  • HarmonyOS PreviewKit 文件预览功能入门与后端示例
  • 开源 ROS 智能割草机器人:技术架构与 DIY 实践
  • C++ 模板详解:非类型参数与特化
  • WebUI LiuMo Batch:AIGC 工作流批量生成平台解析
  • 基于大模型的聊天助手构建案例与优化实践
  • 6 款 AI 测试技能工具,助力自动化测试提效
  • OpenClaw 原理详解:自托管 AI 网关架构深度剖析
  • 用 Spring AI 走通 RAG:从文档切割到检索增强的实战拆解
  • 基于 Java 和 Leaflet 的湖南省道路长度 WebGIS 系统构建
  • Qwen2.5 思维链微调实战:多卡 LoRA 完整代码示例
  • 星辰 RPA 与 Agent:构建小红书自动发文机器人
  • WebStorm 安装与首次配置指南
  • ESP8266 Web 配网与 MQTT 通信方案:免 AT 指令串口透传
  • 封装红黑树实现自定义 set 与 map 容器

相关免费在线工具

  • 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