兼容pc端和移动端的滑块校验

news2024/9/25 13:19:50
组件
<template>
  <canvas :class="cvsClass" :width="props.width" :height="props.height" ref="cvs"></canvas>
</template>

<script setup>
import { ref, reactive, watch, nextTick } from "vue";
const props = defineProps({
  // 是否开启服务端验证
  servertest: {
    type: Boolean,
    default: false
  },
  width: {
    type: Number,
    default: 220
  },
  height: {
    type: Number,
    default: 45
  },
  strokeWidth: {
    type: Number,
    default: 3
  },
  // 滑块宽度
  dropWidth: {
    type: Number,
    default: 50
  },
  // 已激活验证背景色 activeBgColor | 验证中激活的背景色 testIngBgColor| 验证成功激活的背景色 successBgColor
  // 验证成功文本色 tipSucColor| 验证失败文本色 tipTailColor | 验证中的文本色 tipTestIngColor | 待验证文本色 tipNoneColor
  // 移动滑块背景色 dropColor
  // 滑块原始背景色  slideColor
  // 滑块颜色
  dropColor: {
    type: String,
    default: "#fff"
  },
  // 待验证文本色
  tipNoneColor: {
    type: String,
    default: "#000"
  },
  // 验证成功文本色
  tipSucColor: {
    type: String,
    default: "#fff"
  },
  // 验证中的文本色
  tipTestIngColor: {
    type: String,
    default: "#fff"
  },
  // 验证失败文本色
  tipTailColor: {
    type: String,
    default: "#ee0a24"
  },
  // 验证中提示
  testTip: {
    type: String,
    default: "正在验证..."
  },
  // 滑块背景色颜色
  slideColor: {
    type: String,
    default: "#fff"
  },
  // 滑块背景色颜色
  tipTxt: {
    type: String,
    default: "拖动滑块验证"
  },
  // 验证通过背景色
  successBgColor: {
    type: String,
    default: "#07c160"
  },
  //  验证失败背景色
  tailBgColor: {
    type: String,
    default: "#ee0a24"
  },
  // 已激活的背景色
  activeBgColor: {
    type: String,
    default: "#888"
  },
  // 验证中激活的背景色
  testIngBgColor: {
    type: String,
    default: "#ff976a"
  },
  // 验证成功文字提示
  successTip: {
    type: String,
    default: "恭喜你验证通过!"
  },
  // 验证失败文字提示
  failTip: {
    type: String,
    default: "验证失败,请重试"
  },
  // 文本大小
  fontSize: {
    type: Number,
    default: 14
  }
});
const emit = defineEmits(["statu"]);
let vfcx = null;
const cvs = ref();
const cvsClass = ref("cur-none");
let vfcres = {
  startX: 0, //开始拖动滑块位置
  endX: 0, //结束拖动滑块位置
  timed: 0, //拖动所用时间 || 低于30毫秒认定为机器
  guiji: [], //拖动轨迹 | 连续2个2数之差相同判定为机器
  width: props.width
};
const vfcStatu = reactive({
  statu: "none"
});
// 监听数据,并发给父级
watch(vfcStatu, res => {
  emit("statu", res, vfcres);
  // 验证成功
  if (res.statu === "success") {
    vfcx.anmateOff = false;
    vfcx.activeBgColor = props.successBgColor;
    vfcx.tipTxt = props.successTip;
    vfcx.colors.slideColor = props.successBgColor;
    vfcx.evNone();
  } else if (res.statu === "tail") {
    vfcx.reset();
    vfcx.tipTxt = props.failTip;
    vfcx.fontColor = props.tipTailColor;
    vfcx.draw();
  }
});
/**
 * 验证器
 * @param {Element} cvsEl canvas元素
 * @param {String, default:'cur-none'} cvsClass canvas的class
 * @param {Boolear, default:fasle} vfcres 验证结果
 * @param {Number, default:5} strokeWidth 滑块内边距
 * @param {Number,default:50} dropWidth 滑块宽度
 * @param {color,default:'#fff'} dropColor 移动滑块背景色
 * @param {color,default:'#e8e8e8'} slideColor 滑块背景色颜色
 * @param {color,default:'skyblue'} activeBgColor 已激活验证背景色
 * @param {color,default:'#ff976a'} testIngBgColor 验证中激活的背景色
 * @param {color,default:'#07c160'} successBgColor 验证成功激活的背景色
 * @param {color,default:'#07c160'} tipSucColor 验证成功文本色
 * @param {color,default:'#ee0a24'} tipTailColor 验证失败文本色
 * @param {color,default:'#fff'} tipTestIngColor 验证中的文本色
 * @param {color,default:'#000'} tipNoneColor 待验证文本色
 * @param {String,default:'向右滑动验证'} tipTxt 文字提示
 * @param {String,default:'太棒了,恭喜你验证通过!'} successTip 验证成功文字提示
 * @param {String,default:'验证失败,请重试...'} failTip 验证失败文字提示
 * @param {Bool} servertest 是否开启前端验证模式
 * @param {String} testTip 验证提示
 */
class Vfcs {
  constructor(cvsEl, cvsClass, vfcres, vfcStatu, strokeWidth, dropWidth, fontSize, servertest, colors, tipTxt) {
    this.cvsEl = cvsEl;
    this.vfcres = vfcres;
    this.cvsClass = cvsClass;
    this.strokeWidth = strokeWidth;
    this.dropWidth = dropWidth;
    this.vfcStatu = vfcStatu;
    this.colors = colors;
    this.fontSize = fontSize;
    this.dwonIsPath = false; //是否按下验证滑块
    this.ctx = null;
    this.allTipTxts = tipTxt;
    this.tipTxt = this.allTipTxts.tipTxt;
    this.fontColor = this.colors.tipNoneColor;
    this.activeBgColor = this.colors.activeBgColor;
    this.servertest = servertest;
    this.guiji = [];
    this.startTime = 0;
    this.endTime = 0;
    this.startX = 0;
    this.startY = 0;
    this.moveX = 0;
    this.moveY = 0;
    this.fontOp = 1; //文本透明度
    this.met = false;
    this.offX = 0; //x轴的位移
    this.minX = this.strokeWidth / 2;
    this.maxX = this.cvsEl.width - this.dropWidth - this.strokeWidth;
    // this.dropX最大值 -》  cW - this.dropWidth - this.strokeWidth / 2
    // this.dropX最小 -》   this.strokeWidth / 2
    this.dropX = this.minX + this.offX; // 滑块位置
    this.toTouchEnd = false;
    //是否按下滑块
    this.isDown = false;
    this.testAm = null; //验证中动画的id
    this.anmateOff = true; //动画开关
    this.evsName = []; //事件名
    this.evsFun = [this.down.bind(this), this.move.bind(this), this.up.bind(this)]; //事件方法
    this.init();
  }
  init() {
    this.ctx = this.cvsEl.getContext("2d");
    this.draw();
    this.evsName = this.evType();
    // 给canvas添加事件
    this.evsName.forEach((evName, i) => (i === 0 ? this.cvsEl.addEventListener(evName, this.evsFun[i]) : document.addEventListener(evName, this.evsFun[i])));
  }
  // 绘制
  draw() {
    let cW = this.cvsEl.width,
      cH = this.cvsEl.height,
      c = this.ctx;
    c.clearRect(0, 0, cW, cH);
    c.globalAlpha = this.fontOp; // 设置图像透明度
    c.fillRect(0, 0, cW, cH);
    c.fillStyle = this.colors.slideColor;
    c.strokeStyle = this.colors.slideColor;
    c.lineWidth = this.strokeWidth;
    c.fillRect(0, 0, cW, cH);
    c.strokeRect(0, 0, cW, cH);
    // 激活背景色
    c.fillStyle = this.activeBgColor;
    c.strokeStyle = this.activeBgColor;
    c.fillRect(this.minX + 2, this.minX, this.offX, cH - this.strokeWidth);
    // 文本提示
    c.textAlign = "center";
    c.textBaseline = "middle";
    c.fillStyle = this.fontColor;
    c.font = `${this.fontSize}px 黑体`;
    c.fillText(this.tipTxt, cW / 2, cH / 2);
    // 验证失败
    // 待验证 | 验证中
    if (this.vfcStatu.statu === "none" || this.vfcStatu.statu === "testing" || this.vfcStatu.statu === "servertest" || this.vfcStatu.statu === "tail") {
      // 滑块
      c.beginPath();
      c.fillStyle = this.colors.dropColor;
      c.rect(this.dropX, this.minX, this.dropWidth, cH - this.strokeWidth);
      c.fill();
      // 箭头
      c.lineWidth = 2;
      // 右边箭头
      c.moveTo(this.dropX + this.dropWidth / 1.7 - 5, this.strokeWidth + 10);
      c.lineTo(this.dropX + this.dropWidth / 1.7 + 5, cH / 2);
      c.lineTo(this.dropX + this.dropWidth / 1.7 - 5, cH - this.strokeWidth - 10);
      // 左边箭头
      c.moveTo(this.dropX + this.dropWidth / 1.7 - 15, this.strokeWidth + 10);
      c.lineTo(this.dropX + this.dropWidth / 1.7 - 5, cH / 2);
      c.lineTo(this.dropX + this.dropWidth / 1.7 - 15, cH - this.strokeWidth - 10);
      c.stroke();
      c.closePath();
      // 验证成功
    } else if (this.vfcStatu.statu === "success") {
      // 滑块
      c.beginPath();
      c.fillStyle = this.colors.dropColor;
      c.rect(this.dropX, this.minX, this.dropWidth, cH - this.strokeWidth);
      c.fill();
      c.closePath();
      // 圈
      c.beginPath();
      c.fillStyle = this.colors.successBgColor;
      c.arc(this.dropWidth / 2 + this.dropX, cH / 2, cH / 3, 0, 2 * Math.PI);
      c.fill();
      c.closePath();
      // 勾
      c.beginPath();
      c.lineWidth = 3;
      c.lineJoin = "bevel";
      c.lineCap = "round";
      c.strokeStyle = this.colors.dropColor;
      c.moveTo(this.dropX + this.dropWidth / 2 - 8, cH / 2 + 1);
      c.lineTo(this.dropX + this.dropWidth / 2.1, cH / 1.6);
      c.lineTo(this.dropX + this.dropWidth / 2 + 8, cH / 2 - 5);
      c.stroke();
      c.closePath();
    }
  }
  // 滑块按下
  down(ev) {
    if (this.vfcStatu.statu === "testing" || this.vfcStatu.statu === "servertest") return;
    this.setXY(ev);
    //按下滑块
    this.isDown = true;
    this.startTime = new Date().getTime();
    // 若按下滑块
    const isPath = this.ctx.isPointInPath(this.startX, this.startY);
    this.dwonIsPath = isPath;
  }
  // 滑块移动
  move(ev) {
    if (this.vfcStatu.statu === "testing" || this.vfcStatu.statu === "servertest") return;
    this.setXY(ev);
    const isPath = this.ctx.isPointInPath(this.moveX, this.moveX);
    // pc 鼠标变手势
    if (ev.x) isPath === true ? (this.cvsClass.value = "pointer") : (this.cvsClass.value = "cur-none");
    const x = Number(this.moveX.toFixed(2));
    const y = Number(this.moveY.toFixed(2));
    const moveTime = new Date().getTime();
    this.guiji.push({ x, y, moveTime });
    if (this.dwonIsPath === false || this.moveX <= 0) return;
    if (this.isDown === true) {
      // 若滑到尾部
      this.toTouchEnd = this.touchDrosToEnd();
      if (this.toTouchEnd === true) this.up();
      this.draw();
    }
  }
  // 滑块抬起
  up() {
    if (this.vfcStatu.statu === "testing" || this.vfcStatu.statu === "servertest" || this.offX === 0 || this.dwonIsPath === false || this.moveX <= 0) return;
    this.endTime = new Date().getTime();
    this.vfcres.startX = this.startX; //鼠标/手指按下位置
    this.vfcres.endX = this.dropX + this.dropWidth + this.minX; //鼠标/手指抬起位置
    this.vfcres.timed = this.endTime - this.startTime; //耗时
    this.vfcres.guiji = this.guiji; //滑动轨迹
    this.vfcres.width = this.cvsEl.width;
    this.dwonIsPath = false;
    this.isDown = false;
    // 未滑动到尾部
    if (this.toTouchEnd === false) {
      this.dropX = this.minX; // 滑块位置
      this.offX = 0;
      this.tipTxt = this.allTipTxts.failTip;
      this.fontColor = this.colors.tipTailColor;
      // 滑动到尾部
    } else {
      this.vfcStatu.statu = "testing";
      this.testAdmate(); //开启动画
      // 验证中
      this.fontColor = this.colors.tipTestIngColor;
      this.tipTxt = this.allTipTxts.testTip;
      this.activeBgColor = this.colors.testIngBgColor;
      this.dropX = this.maxX + this.minX; // 滑块位置
      const test = this.testVer();
      setTimeout(() => {
        // 前端验证通过
        if (test === "success") {
          // 已开启前端验证模式
          if (this.servertest === true) {
            this.vfcStatu.statu = "servertest";
          } else {
            this.vfcStatu.statu = "success";
          }
          // 前端验证不通过
        } else {
          this.vfcStatu.statu = "tail";
        }
      }, 1000);
    }
    this.draw();
    this.guiji = [];
  }
  // 重置滑块
  reset() {
    this.dropX = this.minX; // 滑块位置
    this.anmateOff = false;
    this.activeBgColor = this.colors.activeBgColor;
    this.fontColor = this.colors.tipNoneColor;
    this.tipTxt = this.allTipTxts.tipTxt;
    this.offX = 0;
    this.toTouchEnd = false;
    this.guiji = [];
    this.draw();
  }
  // 解绑事件
  evNone() {
    this.evsName.forEach((evName, i) => (i === 0 ? this.cvsEl.removeEventListener(evName, this.evsFun[i]) : document.removeEventListener(evName, this.evsFun[i])));
  }
  // 验证中动画
  testAdmate() {
    // 文本透明度
    if (this.met === false && this.fontOp >= 1) {
      this.met = true;
    } else if (this.met === true && this.fontOp <= 0.5) {
      this.met = false;
    }
    this.met === false ? (this.fontOp += 0.015) : (this.fontOp -= 0.015);
    this.draw();
    cancelAnimationFrame(this.testAm);
    this.testAm = window.requestAnimationFrame(this.testAdmate.bind(this));
    if (this.anmateOff === false) {
      cancelAnimationFrame(this.testAm);
      this.fontOp = 1;
      this.testAm = null;
      this.met = false;
      this.anmateOff = true;
    }
    this.draw();
  }
  /**
   * 验证是否滑动到尾部
   * @return {Number}  return true 到尾部,false 没到尾部
   */
  touchDrosToEnd() {
    const x = this.offX + this.dropWidth + this.strokeWidth;
    const isSuccess = x >= this.cvsEl.width;
    return isSuccess;
  }
  // 设置xy坐标
  setXY(ev) {
    if (ev.type === "touchstart") {
      this.startX = ev.touches[0].clientX - this.cvsEl.getBoundingClientRect().left;
      this.startY = ev.touches[0].clientY - this.cvsEl.getBoundingClientRect().top;
    }
    if (ev.type === "touchmove") {
      this.moveX = ev.touches[0].clientX - this.cvsEl.getBoundingClientRect().left;
      this.moveY = ev.touches[0].clientY - this.cvsEl.getBoundingClientRect().top;
    }
    // ///pc事件 //
    if (ev.type === "mousedown") {
      this.startX = ev.x - this.cvsEl.getBoundingClientRect().left;
      this.startY = ev.y - this.cvsEl.getBoundingClientRect().top;
    }
    if (ev.type === "mousemove") {
      this.moveX = ev.x - this.cvsEl.getBoundingClientRect().left;
      this.moveY = ev.y - this.cvsEl.getBoundingClientRect().top;
    }
    // 防止滑块溢出指定范围
    if (ev.type === "mousemove" || ev.type === "touchmove") {
      this.offX = this.moveX - this.startX;
      if (this.offX > this.maxX) this.offX = this.maxX;
      if (this.offX < this.minX) this.offX = this.minX;
      this.dropX = this.minX + this.offX; // 滑块位置
    }
  }
  // 事件类型
  evType() {
    const isMobile = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i) !== null;
    const events = isMobile ? ["touchstart", "touchmove", "touchend"] : ["mousedown", "mousemove", "mouseup"];
    return events;
  }
  /**
   * 滑动轨迹信息 | 计算滑动轨迹每2数之间的差值 | 出现次数等
   * @return {Object(chaArr,repeatX,repeatY,repeatMaxXCount,repeatMaxYCount,allCount)} chaArr → 每2数之间的插值 | repeatX → x轴每2数之间的差值与重复数  | repeatY → y轴每2数之间的差值与重复数 |  repeatMaxXCount → x轴每重复数最多的次数 | repeatMaxYCount → y轴每重复数最多的次数
   */
  arrCmp() {
    // 重复的数量
    const repeatX = [];
    const repeatY = [];
    const timed = [];
    const chaArr = this.guiji.reduce((prev, itm, i, arr) => {
      if (i === arr.length - 1) return prev;
      const nv = arr[i + 1];
      const chaX = Number((nv.x - itm.x).toFixed(2));
      const chaY = Number((nv.y - itm.y).toFixed(2));
      const timeCha = nv.moveTime - itm.moveTime;
      timed.push(timeCha); //时间差
      // 是否有重复的数组
      const repeatXIndex = repeatX.findIndex(item => item.num === chaX);
      const repeatYIndex = repeatY.findIndex(item => item.num === chaY);
      // xy轴每2数差数据
      if (repeatXIndex === -1) {
        const obj = {
          num: chaX,
          count: 1
        };
        repeatX.push(obj);
      } else {
        repeatX[repeatXIndex].count++;
      }
      if (repeatYIndex === -1) {
        const obj = {
          num: chaY,
          count: 1
        };
        repeatY.push(obj);
      } else {
        repeatY[repeatYIndex].count++;
      }
      prev.push({ x: chaX, y: chaY });
      return prev;
    }, []);
    // 所有重复次数
    const findXCount = [];
    const findYCount = [];
    repeatX.forEach(it => findXCount.push(it.count));
    repeatY.forEach(it => findYCount.push(it.count));
    const repeatMaxXCount = Math.max(...findXCount); //x重复最多的次数
    const repeatMaxYCount = Math.max(...findYCount); //y重复最多的次数
    const repeatMaxTimed = Math.max(...timed); //滑动时间重复最多的次数
    return {
      chaArr,
      repeatX,
      repeatY,
      repeatMaxXCount,
      repeatMaxYCount,
      repeatMaxTimed
    };
  }
  // 前端验证
  //  x轴最大波动大于数等于所有波动长度则为人机 | y轴最大波动数等于所有波动长度则为人机 | 滑动时间低于50毫秒不通过  | 时间波动最大次数大于滑动轨迹长度的3/1为人机
  testVer() {
    // return 'tail'
    // 滑动所用时间低于50毫秒 是人机
    if (this.vfcres.timed < 50) return "tail";
    const sliderInfo = this.arrCmp(); //处理滑动轨迹信息
    // 时间波动最大次数等于sliderInfo.chaArr.length滑动轨迹长度为人机
    const timeTest = sliderInfo.repeatMaxTimed === sliderInfo.chaArr.length;
    if (timeTest === true) return "tail";
    // x轴最大波动大于数等于所有波动长度则为人机
    if (sliderInfo.repeatMaxXCount === sliderInfo.repeatX) return "tail";
    //  y轴最大波动数等于所有波动长度则为人机
    if (sliderInfo.repeatMaxYCount === sliderInfo.chaArr.length) return "tail";
    // 是真人
    return "success";
  }
}
nextTick(() => {
  const colors = {
    activeBgColor: props.activeBgColor,
    testIngBgColor: props.testIngBgColor,
    successBgColor: props.successBgColor,
    tipSucColor: props.tipSucColor,
    tipTailColor: props.tipTailColor,
    tipTestIngColor: props.tipTestIngColor,
    tipNoneColor: props.tipNoneColor,
    dropColor: props.dropColor,
    slideColor: props.slideColor
  };
  const tipTxt = {
    testTip: props.testTip,
    tipTxt: props.tipTxt,
    successTip: props.successTip,
    failTip: props.failTip
  };
  vfcx = new Vfcs(cvs.value, cvsClass, vfcres, vfcStatu, props.strokeWidth, props.dropWidth, props.fontSize, props.servertest, colors, tipTxt);
});
</script>

页面调用
<slider-vfc @statu="slideVerify" />
// 滑块验证
const slideVerify = vfcStatu => {
  props.form.verify = vfcStatu.statu == "success";
};

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

力扣: 四数相加II

文章目录 需求代码结尾 需求 给你四个整数数组 nums1、nums2、nums3 和 nums4 &#xff0c;数组长度都是 n &#xff0c;请你计算有多少个元组 (i, j, k, l) 能满足&#xff1a; 0 < i, j, k, l < n nums1[i] nums2[j] nums3[k] nums4[l] 0 示例 1&#xff1a; 输入…

论文解析二: SuperGlue 同时进行特征匹配以及滤除外点的网络

目录 1.SuperGlue摘要2.SuperGlue网络结构2.1 Attentional Graph Neural Network&#xff08; 注意图神经网络&#xff09;2.1.1 KeyPoint Encoder &#xff1a;解决 同时进行特征匹配以及滤除外点的网络2.1.2 Attentional Aggregation 2.2 Optimal Matching Layer (最优匹配层…

ctfshow-PHP反序列化

web254 源码 <?php/* # -*- coding: utf-8 -*- # Author: h1xa # Date: 2020-12-02 17:44:47 # Last Modified by: h1xa # Last Modified time: 2020-12-02 19:29:02 # email: h1xactfer.com # link: https://ctfer.com //mytime 2023-12-4 0:22 */ error_reporting(0)…

【大模型结构】不同技术架构的区别

文章目录 大语言模型技术概述Encoder-onlyDecoder-onlyEncoder-Decoder为什么流行LLM都是Decoder only架构&#xff1f;参考资料 如果你投递的是NLP方向&#xff0c;大模型方向&#xff0c;多模态大模型方向&#xff0c;甚至是人工智能方向&#xff0c;当前大语言模型的技术架构…

基于人工智能的智能个人健康管理系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据采集与预处理模型训练与预测实时健康监控应用场景结论 1. 引言 个人健康管理在现代生活中变得尤为重要&#xff0c;随着人工智能技术的进步&#xff0c;智能健康管理系统可以通过监…

[AI书籍分享]<AI时代,学什么,怎么学 - 和渊>

本文由Markdown语法编辑器编辑完成。 1, 背景: 本书是一位清华大学毕业的生物学博士&#xff0c;和渊老师&#xff0c;现就职于人大附中, 是一名一线的高中生物教师. 她之前已经写过几本关于教育类的书籍&#xff0c;而这本书&#xff0c;则是她针对当前, AI时代迅猛发展的背…

CPU调度算法之FIFS(先来先服务)

摘要 CPU的先来先服务&#xff08;FCFS, First-Come, First-Served&#xff09;任务调度算法是一种最基础且直观的调度方法&#xff0c;它根据任务到达的顺序决定其执行的先后顺序。这种算法以其简单性和公平性在操作系统中占据重要地位&#xff0c;但也存在一些不足&#xff…

【数学建模国赛】前期准备

这里是阿川的博客&#xff0c;祝您变得更强 ✨ 个人主页&#xff1a;在线OJ的阿川 &#x1f496;文章专栏&#xff1a;国赛数模准备到进阶 &#x1f30f;代码仓库&#xff1a; 写在开头 现在您看到的是我的结论或想法&#xff0c;省略了思考过程&#xff0c;但在这背后凝结了大…

测试3个月,成功入职 “字节”,我的面试心得总结!

今天来给大家讲一下软件测试工程师的面试一些技巧、建议&#xff0c;以及你们在面试过程中需要做的一些准备、注意事项。 很多的小伙伴在刚刚学习完软件测试后就要面临一个问题&#xff1a;就业找工作。找工作要面临的第一件事儿就是面试&#xff0c;很多小伙伴对面试完全是模糊…

【C语言】八大排序实现及稳定性和总结

目录 一、八大排序剖析链接二、排序的稳定性2.1 稳定性作用2.2 如何判断排序算法是否稳定 总结三、八大排序实现及源代码3.1 插入排序3.2 希尔排序3.3 堆排序3.4 快速排序3.4.1 Hoare版&#xff08;左右指针法&#xff09;3.4.2 挖坑法3.4.3 前后指针法3.4.4 三数取中优化3.4.5…

CSP-CCF★★201809-2买菜★★

目录 一、问题描述 二、解答&#xff1a; 三、总结 一、问题描述 问题描述 小H和小W来到了一条街上&#xff0c;两人分开买菜&#xff0c;他们买菜的过程可以描述为&#xff0c;去店里买一些菜然后去旁边的一个广场把菜装上车&#xff0c;两人都要买n种菜&#xff0c;所以也…

MySQL系列—9.Innodb页

目录 磁盘与内存交互的基本单位—页 页结构概述 页的大小 页的上层结构 页的内部结构 1、File Header&#xff08;文件头部&#xff09; 2、File Trailer&#xff08;文件尾部&#xff09; 3、Free Space (空闲空间) 4、User Records (用户记录) 5、Infimum Supremu…

「漏洞复现」通天星CMSV6车载定位监控平台 getAlarmAppealByGuid SQL注入漏洞

0x01 免责声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;作者不为此承担任何责任。工具来自网络&#xff0c;安全性自测&#xff0c;如有侵权请联系删…

AI基础 L15 Constraint Satisfaction Problems III约束满足问题

Local Search for CSPs Local search algorithms use a complete-state formulation where: — each state assigns a value to every variable, and — the search changes the value of one variable at a time • Min-conflicts heuristic: value that results in the min…

IEC103设备数据 转 IEC61850项目案例

目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 准备工作 2 4 配置VFBOX网关采集103设备数是 2 5 用IEC61850协议转发数据 4 6 网关使用多个逻辑设备和逻辑节点的方法 6 7 IEC103协议说明 8 8 案例总结 9 1 案例说明 设置网关采集IEC103设备数据把采集的数据转成IEC61850协议转发…

三.海量数据实时分析-FlinkCDC实现Mysql数据同步到Doris

FlinkCDC 同步Mysql到Doris 参考&#xff1a;https://nightlies.apache.org/flink/flink-cdc-docs-release-3.0/zh/docs/get-started/quickstart/mysql-to-doris/ 1.安装Flink 下载 Flink 1.18.0&#xff0c;下载后把压缩包上传到服务器&#xff0c;使用tar -zxvf flink-xxx…

华为认证 vs 红帽认证 怎么选?有什么区别?

随着技术的日新月异&#xff0c;IT认证成为衡量个人技能和专业知识的重要标准。在众多认证中&#xff0c;华为认证和红帽认证以其权威性和实用性&#xff0c;成为业界颇具含金量的标杆。华为认证&#xff0c;作为华为推出的认证&#xff0c;是网络技术领域的权威认证之一&#…

vscode 中使用 yarn 出错

问题 vscode 中使用 yarn 爆红&#xff0c;类似下图的错误&#xff1a; 原因 由于vscode中的集成终端使用的是powershell&#xff0c;所以需要设置下该权限才能正常使用yarn 解决 找到 powershell&#xff0c;以管理身份运行 输入&#xff1a;set-ExecutionPolicy Remot…

MySQL系列—8.物理结构

目录 1.系统表空间 ibdata 2.通用表空间 .ibd 3.独立表空间 4.Undo 表空间 5.临时表空间 6.Redo Log File 1.系统表空间 ibdata 系统表空间由参数innodb_data_file_path定义路径、初始化大小、自动扩展策略 如&#xff1a; innodb_data_file_path/dayta/mysql/ibdata1:…