有趣且重要的JS知识合集(19)前端实现图片的本地上传/截取/导出

news2024/10/7 17:33:26

input[file]太丑了,又不想去改button样式,那就自己实现一个上传按钮的div,然后点击此按钮时,去触发file上传的事件, 以下就是 原生js实现图片前端上传 并且按照最佳宽高比例展示图片,然后可以自定义截取图片,右侧预览区域 可以看到截图,最后还可以导出图片

1、效果图:

左侧为编辑区域,右侧为预览区域

2、文件目录 

3、实现源码: 

1、index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>image-cut</title>
  <link rel="stylesheet" href="./assets/reset.css" />
  <link rel="stylesheet" href="./assets/main.css" />
  <link rel="stylesheet" href="./assets/rect.css" />
</head>
<!-- 图片裁剪 -->
<body>
  <div id="root">
    <div id="tool" class="tool">
      <span id="insert-img" title="插入你想要截图的图片">插入图片</span>
      <span id="start-paint" title="绘制矩形框并在右侧展示区生成canvas">开始截图</span>
      <span id="clear" title="清除矩形框并清除右侧截图">清除截图</span>
      <span id="export-clip" title="将右侧截图下载到本地">导出截图</span>
    </div>
    <div class="container">
      <div id="control" class="area">
        <div id="rect" class="rect">
          <span class="left-top dot"></span>
          <span class="middle-top dot"></span>
          <span class="right-top dot"></span>
          <span class="right-middle dot"></span>
          <span class="right-bottom dot"></span>
          <span class="middle-bottom dot"></span>
          <span class="left-bottom dot"></span>
          <span class="left-middle dot"></span>
        </div>
        <div class="img-box">
          <img />
        </div>
      </div>
      <div id="display" class="area display">
        <canvas></canvas>
      </div>
    </div>
  </div>
  <!-- 矩形框脚本 -->
  <script type="module" src="./srcipt/rect.js"></script>
  <!-- 主控制区脚本 -->
  <script type="module" src="./srcipt/main.js"></script>
</body>
</html>

2、/script/rect.js

import {
  methods
} from './main.js'

const dom = document.querySelector('#control')
const rect  = document.querySelector('#rect')
const origin = dom.getBoundingClientRect()
const parentBorder = Number(getComputedStyle(dom, null).borderWidth.split('px')[0]) // 父元素边框 如果你明确知道边框宽度,就不需要这行,直接赋值就行
const childBorder = Number(getComputedStyle(rect, null).borderWidth.split('px')[0]) // 子元素边框 如果你明确知道边框宽度,就不需要这行,直接赋值就行
let finallPoint
/**
 * 开始绘制
 */
const startMouse = () => {
  dom.style.cursor = 'crosshair'
  dom.onmousedown = e => {
    if (e.target !== dom) return
    const left = e.offsetX
    const top = e.offsetY
    rect.style.left = left + 'px'
    rect.style.top = top + 'px'
    rect.style.borderColor = getCurrentColor() // 绘制时使用选择的框的颜色
    const childs = rect.children
    for (let i = 0; i < childs.length; i++) {
      childs[i].style.borderColor = getCurrentColor() // 绘制时使用选择的框的颜色
    }
    dom.onmousemove = e => {
      // 宽高边界限制
      const widthArea = e.clientX - origin.x > dom.offsetWidth - (parentBorder * 2) ? dom.offsetWidth - (parentBorder * 2) : e.clientX - origin.x
      const heightArea = e.clientY - origin.y > dom.offsetHeight - (parentBorder * 2) ? dom.offsetHeight - (parentBorder * 2) : e.clientY - origin.y
      rect.style.width = widthArea - left + 'px'
      rect.style.height = heightArea - top + 'px'
    }
    dom.onmouseup = e => {
      generatePoint()
      dom.onmousedown = null
      dom.onmousemove = null
      dom.onmouseup = null
      dom.style.cursor = ''
      editMouse()
    }
  }
}
const editMouse = () => {
  rect.onmousedown = e => {
    if (e.target !== rect && e.target.className.indexOf('dot') === -1) return // 类名中包含被放行的dot除外
    const flag = mousedownHandle(e)
    let left = e.clientX
    let top = e.clientY
    const width = rect.offsetWidth
    const height = rect.offsetHeight
    const [dragX, dragY] = flag
    // 拖动
    if (dragX === -1 && dragY === -1) {
      left -= rect.offsetLeft // 要保持之前矩形框的坐标值
      top -= rect.offsetTop
    }
    const child = e.target.getBoundingClientRect()
    document.onmousemove = e => {
      // 取消浏览器因回流导致的默认事件及冒泡事件
      e.preventDefault()
      if (e.stopPropagation) {
        e.stopPropagation()
      } else {
        e.cancelable = true
      }
      finallPoint = {
        left: 0,
        top: 0,
        width: 0,
        height: 0
      }
      if (dragX === -1 && dragY === -1) {
        rect.style.cursor = 'move'
        const rightArea = dom.offsetWidth - rect.offsetWidth // 右边界
        const bottomArea = dom.offsetHeight - rect.offsetHeight // 下边界
        const leftArea = 0 // 左边界
        const topArea = 0 // 上边界
        finallPoint.left = e.clientX - left > rightArea ? rightArea : (e.clientX - left< leftArea ? leftArea : e.clientX - left)
        finallPoint.top = e.clientY - top > bottomArea ? bottomArea : (e.clientY - top < topArea ? topArea : e.clientY - top)
        rect.style.left = finallPoint.left + 'px'
        rect.style.top = finallPoint.top + 'px'
      } else if (dragX === 0 && dragY === 0) { // 左上角拉伸
        finallPoint.left = e.clientX > origin.x ? ((e.clientX > (left + width)) ? left + width - origin.x : e.clientX - origin.x) : 0
        finallPoint.top = e.clientY > origin.y ? ((e.clientY > (top + height)) ? top + height - origin.y : e.clientY - origin.y) : 0
        finallPoint.width = e.clientX > origin.x ? ((e.clientX > (left + width)) ? 0 : width + (left - e.clientX)) : width + (left - origin.x)
        finallPoint.height = e.clientY > origin.y ? ((e.clientY > (top + height)) ? 0 : height + (top - e.clientY)) : height + (top - origin.y)
        rect.style.left = finallPoint.left + 'px'
        rect.style.top = finallPoint.top + 'px'
        rect.style.width = finallPoint.width + 'px'
        rect.style.height = finallPoint.height + 'px'
      } else if (dragX === 1 && dragY === 0) { // 中上拉伸
        finallPoint.top = e.clientY > origin.y ? ((e.clientY > (top + height)) ? top + height - origin.y : e.clientY - origin.y) : 0
        finallPoint.height = e.clientY > origin.y ? ((e.clientY > (top + height)) ? 0 : height + (top - e.clientY)) : height + (top - origin.y)
        rect.style.top = finallPoint.top + 'px'
        rect.style.height = finallPoint.height + 'px'
      } else if (dragX === 2 && dragY === 0) { // 右上角拉伸
        finallPoint.top = e.clientY > origin.y ? ((e.clientY > (top + height)) ? top + height - origin.y : e.clientY - origin.y) : 0
        finallPoint.width = (e.clientX - left + width > dom.offsetWidth - rect.offsetLeft - (parentBorder * 2) ? dom.offsetWidth - rect.offsetLeft - (parentBorder * 2) : e.clientX - left + width)
        finallPoint.height = e.clientY > origin.y ? ((e.clientY > (top + height)) ? 0 : height + (top - e.clientY)) : height + (top - origin.y)
        rect.style.top = finallPoint.top + 'px'
        rect.style.width = finallPoint.width + 'px'
        rect.style.height = finallPoint.height + 'px'
      } else if (dragX === 2 && dragY === 1) { // 右中拉伸
        finallPoint.width = (e.clientX - left + width > dom.offsetWidth - rect.offsetLeft - (parentBorder * 2) ? dom.offsetWidth - rect.offsetLeft - (parentBorder * 2) : e.clientX - left + width)
        rect.style.width = finallPoint.width + 'px'
      }else if (dragX === 2 && dragY === 2) { // 右下角拉伸
        finallPoint.width = (e.clientX - left + width > dom.offsetWidth - rect.offsetLeft - (parentBorder * 2) ? dom.offsetWidth - rect.offsetLeft - (parentBorder * 2) : e.clientX - left + width)
        finallPoint.height = (e.clientY- top + height > dom.offsetHeight - rect.offsetTop - (parentBorder * 2) ? dom.offsetHeight - rect.offsetTop - (parentBorder * 2) : e.clientY- top + height)
        rect.style.width = finallPoint.width + 'px'
        rect.style.height = finallPoint.height + 'px'
      } else if (dragX === 1 && dragY === 2) { // 中下拉伸
        finallPoint.height = (e.clientY- top + height > dom.offsetHeight - rect.offsetTop - (parentBorder * 2) ? dom.offsetHeight - rect.offsetTop - (parentBorder * 2) : e.clientY- top + height)
        rect.style.height = finallPoint.height + 'px'
      } else if (dragX === 0 && dragY === 2) { // 左下角拉伸
        finallPoint.left = e.clientX > origin.x ? ((e.clientX > (left + width)) ? left + width - origin.x : e.clientX - origin.x) : 0
        finallPoint.width = e.clientX > origin.x ? ((e.clientX > (left + width)) ? 0 : width + (left - e.clientX)) : width + (left - origin.x)
        finallPoint.height = (e.clientY- top + height > dom.offsetHeight - rect.offsetTop - (parentBorder * 2) ? dom.offsetHeight - rect.offsetTop - (parentBorder * 2) : e.clientY- top + height)
        rect.style.left = finallPoint.left + 'px'
        rect.style.width = finallPoint.width + 'px'
        rect.style.height = finallPoint.height + 'px'
      } else if (dragX === 0 && dragY === 1) { // 左中拉伸
        finallPoint.left = e.clientX > origin.x ? ((e.clientX > (left + width)) ? left + width - origin.x : e.clientX - origin.x) : 0
        finallPoint.width = e.clientX > origin.x ? ((e.clientX > (left + width)) ? 0 : width + (left - e.clientX)) : width + (left - origin.x)
        rect.style.left = finallPoint.left + 'px'
        rect.style.width = finallPoint.width + 'px'
      }
      generatePoint()
    }
    document.onmouseup = e => {
      document.onmousemove = null
      document.onmouseup = null
      rect.style.cursor = 'move'
    }
  }
}
/**
 * mousedown逻辑处理
 */
const mousedownHandle = (e) => {
  let flag = 0 // 点击的是除边角以外的其他部分 拖动
  let startX = e.offsetX
  let startY = e.offsetY
  let width = e.target.offsetWidth
  let height = e.target.offsetHeight
  if (e.target !== rect) {
    flag = 1 // 点击的是边角 缩放
    const parent = e.target.offsetParent.getBoundingClientRect()
    const child = e.target.getBoundingClientRect()
    startX = child.x - parent.x
    startY = child.y - parent.y
    width = e.target.offsetParent.offsetWidth
    height = e.target.offsetParent.offsetHeight
  }
  const difference = 12 // 点击四边角12 px范围为拉伸,其他为拖动,这个值可以根据你需要的来调整
  let left = 0 // 0 => left, 1 => middle, 2 => right, -1 => 点击的位置不能被拖动
  let top = 0 // 0 => top, 1 => middle, 2 => bottom, -1 => 点击的位置不能被拖动
  if (startX < difference) { // 点击的位置为矩形左侧
    left = 0
  } else if (startX > width / 2 - difference && startX < width / 2 + difference) { // 点击的位置为矩形中间 width/2 - 6px ~ width/2 + 6px
    left = 1
  } else if (startX < width && startX > width - difference){ // 点击的位置为矩形右侧 width - 6px ~ width
    left = 2
  } else {
    left = -1
  }

  if (startY < difference) { // 点击的位置为矩形上侧
    top = 0
  } else if (startY > height / 2 - difference && startY < height / 2 + difference) { // 点击的位置为矩形中间 height/2 - 6px ~ height/2 + 6px
    top = 1
  } else if (startY < height && startY > height - difference){ // 点击的位置为矩形下侧 height - 6px ~ height
    top = 2
  } else {
    top = -1
  }
  if (left === -1 || top === -1 || (left === 1 && top === 1)) {
    return [-1, -1]
  }
  return [left, top] // 只会有八个位置能被准确返回,其余都是返回[-1, -1]
}

const clear = document.querySelector('#clear') // 清除截图
const startPaint = document.querySelector('#start-paint') // 开始绘制

const getCurrentColor = () => {
  return '#fa9120'
}
/** 生成最终坐标 */
const generatePoint = () => {
  const rectArgs = {
    left: parseInt(getComputedStyle(rect).left),
    top: parseInt(getComputedStyle(rect).top),
    width: parseInt(getComputedStyle(rect).width),
    height: parseInt(getComputedStyle(rect).height),
  }
  methods.generateImg(rectArgs)
}
/** 清除矩形框 */
export const clearRect = () => {
  rect.style.left = '-9999px'
  rect.style.top = 0
  rect.style.width = 0
  rect.style.height = 0
}

clear.onclick = e => {
  methods.clearCanvas()
  clearRect()
}
startPaint.onclick = e => {
  startMouse()
}

3、/script/main.js

import {
  clearRect
} from './rect.js';

// 编辑区dom
const control = document.querySelector('#control')
// 编辑区显示的图片dom
const controlImg = document.querySelector('#control img')
// 预览区dom
const display = document.querySelector('#display')
// 预览区显示的canvas dom
const canvas = document.querySelector('#display canvas')
const ctx = canvas.getContext('2d')
// 插入图片的dom
const insertImg = document.querySelector('#insert-img')
// 导出截图的dom
const exportClip = document.querySelector('#export-clip')
// 图片对象
let imgObj = null
// 最佳显示比例
let bestScale = 0
const methods = {
  /** ------ 图片上传模块 开始 ------ */
  doInput() {
    const inputObj = document.createElement('input');
    inputObj.addEventListener('change', this.readFile, false);
    inputObj.type = 'file';
    inputObj.accept = 'image/*';
    inputObj.click();
  },
  readFile() {
    const file = this.files[0]; // 获取input输入的图片
    if(!/image\/\w+/.test(file.type)){
        alert("请确保文件为图像类型");
        return false;
    } // 判断是否图片
    const reader = new FileReader();
    reader.readAsDataURL(file); // 转化成base64数据类型
    reader.onload = function(e){
      methods.drawToCanvas(this.result); // lve为当前实例
    }
  },
  drawToCanvas(imgData) {
    imgObj = new Image()
    controlImg.src = imgObj.src = imgData
    imgObj.onload = () => {
      bestScale = methods.calcBestScale(imgObj, control.offsetWidth, control.offsetHeight)
      // 图片按最佳比例展示
      controlImg.width = imgObj.width * bestScale
      controlImg.height = imgObj.height * bestScale
      // 外部盒子也按照最佳比例展示
      control.style.width = controlImg.width + 'px'
      control.style.height = controlImg.height + 'px'
    }
  },
  /** ------ 图片上传模块 结束 ------ */
  /**
   * 随机id
   */
  uuid() {
    let d = new Date().getTime();
    const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      const r = (d + Math.random() * 16) % 16 | 0;
      d = Math.floor(d / 16);
      return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
    return uuid
  },
  /**
   * canvas转base64
   * @param {*} blob
   * @param {*} type
   * @param {*} name
   */
  blob2file(blob, type = 'png', name = '') {
    const fileName = name || this.uuid() + '.' + type
    const file = new File([blob], fileName, { type: blob.type, lastModified: Date.now() })
    return file
  },
  /**
   * 计算最佳的图片显示比例
   * @param {*} img
   * @param {*} deviceWidth
   * @param {*} deviceHeight
   * @returns 
   */
  calcBestScale(img, deviceWidth, deviceHeight) {
    return Math.min(deviceWidth / img.width, deviceHeight / img.height)
  },
  /**
   * 清除canvas
   */
  clearCanvas() {
    canvas && ctx.clearRect(0, 0, display.offsetWidth, display.offsetHeight)
  },
  /**
   * 生成图片
   * @param {*} src 
   */
  generateImg(rect) {
    if (!imgObj) return
    const {
      left,
      top,
      width,
      height
    } = rect
    const displayRect = {
      left: left / bestScale,
      top: top / bestScale,
      width: width / bestScale,
      height: height / bestScale
    }
    // 当截图矩形框宽度大于高度时,以预览区宽度为限制,高度按比例缩放
    if (displayRect.width >  displayRect.height) {
      canvas.width = display.offsetWidth
      canvas.height = display.offsetWidth * displayRect.height / displayRect.width
    // 当截图矩形框高度大于宽度时,以预览区高度为限制,宽度按比例缩放
    } else {
      canvas.height = display.offsetHeight
      canvas.width = display.offsetHeight * displayRect.width / displayRect.height
    }
    ctx.drawImage(imgObj, displayRect.left, displayRect.top, displayRect.width, displayRect.height, 0, 0, canvas.width, canvas.height)
  }
}
/** 点击插入图片触发逻辑 */
insertImg.addEventListener('click', () => {
  clearRect()
  methods.doInput()
})

/** 点击导出截图触发逻辑 */
exportClip.addEventListener('click', () => {
  if (canvas) {
    // 创建一个 a 标签,并设置 href 和 download 属性
    const el = document.createElement('a');
    // 设置 href 为图片经过 base64 编码后的字符串,默认为 png 格式
    el.href = canvas.toDataURL('image/png', 1.0);
    el.download = '截图.png';
    // 创建一个点击事件并对 a 标签进行触发
    const event = new MouseEvent('click');
    el.dispatchEvent(event);
  }
})

export {
  methods
}

4、/assets/rect.css

.rect{
  position: absolute;
  /* box-shadow: 0 0 0 1999px rgba(0, 0, 0, .4); */
  left: -9999px;
  top: 0;
  width: 0;
  height: 0;
  border: 1px solid #d79751;
  cursor: move;
  z-index: 1;
}
.rect > span{
  position: absolute;
  width: 4px;
  height: 4px;
  /* border-radius: 50%; */
  border: 1px solid #fa9120;
  background-color: #fa9120;
}
.rect .left-top{
  left: -3px;
  top: -3px;
  cursor: nwse-resize;
}
.rect .middle-top{
  left: 50%;
  top: -3px;
  transform: translateX(-50%);
  cursor: n-resize;
}
.rect .right-top{
  right: -3px;
  top: -3px;
  cursor: nesw-resize;
}
.rect .right-middle{
  right: -3px;
  top: 50%;
  transform: translateY(-50%);
  cursor: e-resize;
}
.rect .right-bottom{
  right: -3px;
  bottom: -3px;
  cursor: nwse-resize;
}
.rect .middle-bottom{
  left: 50%;
  bottom: -3px;
  transform: translateX(-50%);
  cursor: s-resize;
}
.rect .left-bottom{
  left: -3px;
  bottom: -3px;
  cursor: nesw-resize;
}
.rect .left-middle{
  left: -3px;
  top: 50%;
  transform: translateY(-50%);
  cursor: w-resize;
}

5、/assets/main.css

body {
  position: relative;
  width: 100vw;
  height: 100vh;
}

#root {
  width: 80%;
  height: 624px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

.tool {
  padding: 0 10px;
  line-height: 24px;
  height: 24px;
  width: 100%;
}

.tool span {
  cursor: pointer;
}

.tool span:hover {
  color: #fa9120;
}

.container {
  width: 100%;
  height: calc(100% - 24px);
  position: relative;
  display: flex;
}

.area {
  width: 50%;
  max-width: 50%;
  height: 100%;
  border: 2px dashed #eee;
  border-radius: 8px;
  position: relative;
}

.area .img-box {
  position: absolute;
  left: 0;
  height: 0;
  z-index: -1;
  pointer-events: none;
}

.area img {
  pointer-events: none;
}

#control canvas {
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0;
  top: 0;
  z-index: -1;
}

6、/assets/reset.css(初始化样式表,这个可以你自行实现)

* {
  box-sizing: border-box;
}

body,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
p,
blockquote,
dl,
dt,
dd,
ul,
ol,
li,
pre,
form,
fieldset,
legend,
button,
input,
textarea,
th,
td {
    margin: 0;
    padding: 0;
}

body,
button,
input,
select,
textarea {
    font: 12px/1.5tahoma, arial, \5b8b\4f53;
}

h1,
h2,
h3,
h4,
h5,
h6 {
    font-size: 100%;
}

address,
cite,
dfn,
em,
var {
    font-style: normal;
}

code,
kbd,
pre,
samp {
    font-family: couriernew, courier, monospace;
}

small {
    font-size: 12px;
}

ul,
ol {
    list-style: none;
}

a {
    text-decoration: none;
}

a:hover {
    text-decoration: none;
}

legend {
    color: #000;
}

fieldset,
img {
    border: 0;
}

button,
input,
select,
textarea {
    font-size: 100%;
}

table {
    border-collapse: collapse;
    border-spacing: 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1465656.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cesium入门基础一:cesium加载地球与环境搭建

一、cesium加载地球与环境搭建 1、搭建vue3项目 使用vite进行搭建。 npm init vitelatest根据操作提示选择&#xff1a;vue 3 Typescript 2、下载cesium库 终端输入&#xff1a; npm install cesium3、创建cesium地图容器 <template><div id"view_contai…

C 标准库 - <errno.h>

在C语言编程中&#xff0c;<errno.h> 头文件扮演着至关重要的角色&#xff0c;它提供了一个全局变量 errno 以及一系列预定义宏&#xff0c;用于指示系统调用或库函数执行过程中发生的错误。这些宏有助于程序员诊断和处理运行时错误。 errno 变量 extern int errno;err…

【文件搜索项目】使用jdbc操作SQLite

一. 插入&#xff08;采用变量的方式插入&#xff09; 1.创建数据源.DateSource 2.建立连接 3.构造SQL语句 4.执行SQL语句 5.释放资源 public class TestSQLite {public static void main(String[] args) throws SQLException {textInsert();}public static void textInsert(…

IO进程线程的通信操作

1.编程实现互斥机制 程序代码&#xff1a; 1 #include<myhead.h>2 int num520;//临界资源3 //1.创建一个互斥锁变量4 pthread_mutex_t mutex;//定义一个pthread_mutex_t类型的变量5 //定义任务1函数6 void *task1(void *arg)7 {8 printf("不畏过去\n");9 …

【前端素材】推荐优质后台管理系统GramOs平台模板(附源码)

一、需求分析 后台管理系统是一种用于管理网站、应用程序或系统的工具&#xff0c;它通常作为一个独立的后台界面存在&#xff0c;供管理员或特定用户使用。下面详细分析后台管理系统的定义和功能&#xff1a; 1. 定义 后台管理系统是一个用于管理和控制网站、应用程序或系统…

git切换仓库地址

已有git仓库&#xff0c;要切换提交的仓库地址&#xff0c;用以下命令 git remote set-url origin 自己的仓库地址 用以下命令&#xff0c;查看当前仓库地址&#xff1a; git remote show origin 切换仓库后&#xff0c;用以下命令初始化提交仓库&#xff1a; git push -u o…

cocos creator3.x项目打包成aar 加入到已有的Android工程

Cocos crearor版本&#xff1a; 3.4.2 Android Studio Flamingo | 2022.2.1 Patch 2 1、配置构建安卓项目 2、 运行编译无报错 出现问题可尝试修改Gradle版本 修改jdk版本 3、对libservice打包成aar 打包完后 再build/outputs找到aar 如果看不到Tasks模块&#xff0c;在Fil…

连接查询(学习笔记)

通过对DQL的学习&#xff0c;我们可以很轻松的从一张数据表中查询出需要的数据&#xff1b;在企业的应用开发中&#xff0c;我们经常需要从多张表中查询数据&#xff08;例如&#xff1a;我们查询学生信息的时候需要同时查询学生的班级信息&#xff09;&#xff0c;可以通过连接…

【Spring】IoC容器 控制反转 与 DI依赖注入 概念 第一期

文章目录 Spring 和 SpringFramework概念一、Spring IoC容器 核心概念1.1 组件和组件管理概念1.2 Spring IoC容器和容器实现1.2.1 普通和复杂容器1.2.2 SpringIoC容器介绍1.2.3 SpringIoC容器具体接口和实现类1.2.4 SpringIoC容器管理配置方式 1.3 Spring IoC / DI概念总结二、…

Electron实战之环境搭建

工欲善其事必先利其器&#xff0c;在进行实战开发的时候&#xff0c;我们最终的步骤是搞好一个舒服的开发环境&#xff0c;目前支持 Vue 的 Electron 工程化工具主要有 electron-vue、Vue CLI Plugin Electron Builder、electron-vite。 接下来我们将分别介绍基于 Vue CLI Plu…

DataDreamer:让创建自定义数据集轻松无比!还自带标注!

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 ▌前言 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是Ash…

数字信号处理:傅里叶分析

本文主要参考视频如下&#xff1a; 数字信号处理9-1_线性时不变系统对复指数信号的响应_哔哩哔哩_bilibili 傅里叶分析的主要研究内容如下所示&#xff1a; 注意&#xff0c;计算机中使用的离散傅里叶变换并不是离散时间傅里叶变换&#xff1b; 前四种都是理论上的变换方式&…

Graphpad Prism10.2.0(329) 安装教程 (含Win/Mac版)

GraphPad Prism GraphPad Prism是一款非常专业强大的科研医学生物数据处理绘图软件&#xff0c;它可以将科学图形、综合曲线拟合&#xff08;非线性回归&#xff09;、可理解的统计数据、数据组织结合在一起&#xff0c;除了最基本的数据统计分析外&#xff0c;还能自动生成统…

springboot大学生体质测试管理系统源码和论文

大学生体质测试管理系统提供给用户一个简单方便体质测试管理信息&#xff0c;通过留言区互动更方便。本系统采用了B/S体系的结构&#xff0c;使用了java技术以及MYSQL作为后台数据库进行开发。系统主要分为系统管理员、教师和用户三个部分&#xff0c;系统管理员主要功能包括首…

vue-router 三级路由,路由跳转页面异常白屏或404,或刷新三级路由页面后一级和二级路由菜单丢失

问题描述 情况1. vue-router 定义三级路由&#xff0c;路由跳转了&#xff0c;页面404或者白屏情况2. 点击菜单三级路由后&#xff0c;刷新页面后一级和二级路由菜单丢失 解决方案&#xff1a; 某些时候是因为二级和三级的路由共用router-view&#xff0c;可以使用router-vi…

【MySQL系列 04】深入浅出索引

一、索引介绍 提到数据库索引&#xff0c;相信大家都不陌生&#xff0c;在日常工作中会经常接触到。比如某一个 SQL 查询比较慢&#xff0c;分析完原因之后&#xff0c;你可能就会说“给某个字段加个索引吧”之类的解决方案。 但到底什么是索引&#xff0c;索引又是如何工作的…

Vue | (四)使用Vue脚手架(上) | 尚硅谷Vue2.0+Vue3.0全套教程

文章目录 &#x1f4da;初始化脚手架&#x1f407;创建初体验&#x1f407;分析脚手架结构&#x1f407;关于render&#x1f407;查看默认配置 &#x1f4da;ref与props&#x1f407;ref属性&#x1f407;props配置项 &#x1f4da;混入&#x1f4da;插件&#x1f4da;scoped样…

抖音爬虫批量视频提取功能介绍|抖音评论提取工具

抖音爬虫是指通过编程技术从抖音平台上获取视频数据的程序。在进行抖音爬虫时&#xff0c;需要注意遵守相关法律法规和平台规定&#xff0c;以确保数据的合法获取和使用。 一般来说&#xff0c;抖音爬虫可以实现以下功能之一&#xff1a;批量视频提取。这个功能可以用于自动化地…

Lua速成(2)

一、流程控制 Lua 编程语言流程控制语句通过程序设定一个或多个条件语句来设定。在条件为 true 时执行指定程序代码&#xff0c;在条件为 false 时执行其他指定代码。 控制结构的条件表达式结果可以是任何值&#xff0c;Lua认为false和nil为假&#xff0c;true和非nil为真。 …

模拟、排序(归并排序)算法

模拟、排序算法 一、模拟例题1、错误票据题目信息思路题解 2、回文日期题目信息思路方法一&#xff1a;暴力做法方法二&#xff1a;优化解法 题解方法一&#xff1a;暴力求解方法二&#xff1a;优化解法 二、排序例题1、归并排序题目信息思路题解 一、模拟 例题 1、错误票据 …