Vue3 中用 canvas 封装抽奖转盘组件:设定中奖概率及奖项图标和名称
在 Web 应用开发中,抽奖功能是提升用户参与度的常用手段。使用 Vue3 结合 canvas 技术,我们可以轻松实现一个高度自定义的抽奖转盘组件,不仅能设定中奖概率,还能灵活配置奖项图标和名称。本文将详细介绍该组件的实现原理、步骤,并提供完整代码。
实现原理
抽奖转盘组件的核心在于通过 canvas 绘制转盘图形,并结合动画效果实现转盘的旋转。通过设定不同奖项的中奖概率,随机选择中奖奖项,并使用缓动函数控制转盘旋转的动画效果,使转盘最终停在对应的中奖区域。
实现步骤
1. 定义组件模板
在 Vue3 的单文件组件(.vue)中,定义抽奖转盘组件的模板。模板包含 canvas 画布用于绘制转盘,一个中心按钮用于触发抽奖,以及一个提示框用于展示中奖结果。
<template><div class="lottery-container"><div class="wheel-container"><canvas ref="canvasRef" width="350" height="350"></canvas><div class="wheel-center" @click="startLottery"><div class="start-btn">{{ isRotating? '抽奖中...' : '开始抽奖' }}</div></div><div class="pointer"></div></div><div v-if="result" class="result-modal"><h3>恭喜您获得: {{ result.name }}</h3><button @click="result = null">确定</button></div></div></template>
2. 定义组件属性和数据
使用defineProps定义组件接收的属性,包括奖项配置(prizes)、抽奖持续时间(duration)、转盘颜色(colors)。同时,使用ref定义组件内部状态,如是否正在旋转(isRotating)、旋转角度(rotationAngle)、选中的奖项索引(selectedPrizeIndex)、中奖结果(result)等。
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'const props = defineProps({prizes: {type: Array,required: true,validator: (value) => {return value.length >= 2 && value.every(item =>item.name && typeof item.probability === 'number' && item.probability >= 0)}},duration: {type: Number,default: 5000},colors: {type: Array,default: () => ['#FF5252', '#FF4081', '#E040FB', '#7C4DFF','#536DFE', '#448AFF', '#40C4FF', '#18FFFF','#64FFDA', '#69F0AE', '#B2FF59', '#EEFF41','#FFFF00', '#FFD740', '#FFAB40', '#FF6E40']}})const emit = defineEmits(['start', 'end'])const canvasRef = ref(null)const isRotating = ref(false)const rotationAngle = ref(0)const selectedPrizeIndex = ref(-1)const result = ref(null)const loadedImages = ref({})let animationFrameId = nulllet startTime = 0
3. 预加载图片
如果奖项配置中有图标,需要提前加载图片,确保绘制转盘时图标能正常显示。通过Image对象加载图片,并在onload事件中触发转盘绘制。
const loadImages = () => {props.prizes.forEach((prize, index) => {if (prize.icon) {const img = new Image()img.src = prize.iconimg.onload = () => {loadedImages.value[index] = imgdrawWheel()}img.onerror = () => {console.error(`图片加载失败: ${prize.icon}`)}}})}
4. 绘制转盘
获取 canvas 上下文,根据奖项数量计算每个扇形的角度,绘制转盘的扇形区域,并在每个扇形区域绘制奖项名称和图标。
const drawWheel = () => {const canvas = canvasRef.valueif (!canvas) returnconst ctx = canvas.getContext('2d')const centerX = canvas.width / 2const centerY = canvas.height / 2const radius = Math.min(centerX, centerY) - 10const arc = Math.PI * 2 / props.prizes.lengthctx.clearRect(0, 0, canvas.width, canvas.height)// 绘制扇形props.prizes.forEach((prize, index) => {const startAngle = index * arc + rotationAngle.valueconst endAngle = (index + 1) * arc + rotationAngle.valuectx.beginPath()ctx.fillStyle = props.colors[index % props.colors.length]ctx.moveTo(centerX, centerY)ctx.arc(centerX, centerY, radius, startAngle, endAngle)ctx.closePath()ctx.fill()// 绘制文字和图标ctx.save()ctx.translate(centerX, centerY)ctx.rotate(startAngle + arc / 2)// 绘制文字ctx.fillStyle = '#fff'ctx.font = 'bold 14px Arial'ctx.textAlign = 'center'ctx.fillText(prize.name, radius * 0.7, 5)// 绘制图标if (loadedImages.value[index]) {const img = loadedImages.value[index]ctx.drawImage(img, radius * 0.5 - 15, -15, 30, 30)} else if (prize.icon) {ctx.fillStyle = 'rgba(255,255,255,0.7)'ctx.fillRect(radius * 0.5 - 15, -15, 30, 30)}ctx.restore()})}
5. 开始抽奖和动画效果
实现startLottery方法用于开始抽奖,通过selectPrizeByProbability方法根据概率选择中奖奖项,然后使用animateRotation方法实现转盘旋转的动画效果。动画效果中使用缓动函数控制旋转速度,使转盘先快后慢最终停在中奖区域。
const startLottery = () => {if (isRotating.value) returnemit('start')isRotating.value = trueresult.value = nullselectedPrizeIndex.value = selectPrizeByProbability()startTime = performance.now()animateRotation()}const animateRotation = () => {const now = performance.now()const elapsed = now - startTimeconst progress = Math.min(elapsed / props.duration, 1)// 缓动函数 - 先快后慢const easeOut = 1 - Math.pow(1 - progress, 4)// 计算旋转角度const anglePerItem = Math.PI * 2 / props.prizes.lengthconst fullCircles = 5 // 完整旋转圈数// 关键修正:减去Math.PI/2(90度)来校准初始位置const targetAngle = fullCircles * Math.PI * 2 +(Math.PI * 2 - anglePerItem * selectedPrizeIndex.value) -(anglePerItem / 2) -Math.PI / 2rotationAngle.value = easeOut * targetAngledrawWheel()if (progress < 1) {animationFrameId = requestAnimationFrame(animateRotation)} else {// 确保最终角度精确对准奖项中心rotationAngle.value = fullCircles * Math.PI * 2 +(Math.PI * 2 - anglePerItem * selectedPrizeIndex.value) -(anglePerItem / 2) -Math.PI / 2drawWheel()isRotating.value = falseemit('end', props.prizes[selectedPrizeIndex.value])result.value = props.prizes[selectedPrizeIndex.value]}}
6. 根据概率选择奖项
实现selectPrizeByProbability方法,计算所有奖项的总概率,然后生成一个随机数,根据随机数落在哪个概率区间来确定中奖奖项。
const selectPrizeByProbability = () => {const totalProbability = props.prizes.reduce((sum, prize) => sum + prize.probability, 0)const random = Math.random() * totalProbabilitylet currentSum = 0for (let i = 0; i < props.prizes.length; i++) {currentSum += props.prizes[i].probabilityif (random <= currentSum) {return i}}return 0}
7. 生命周期钩子和监听器
在onMounted钩子中调用loadImages和drawWheel初始化转盘;在onBeforeUnmount钩子中取消动画帧,避免内存泄漏;使用watch监听props.prizes的变化,重新加载图片并绘制转盘。
onMounted(() => {loadImages()drawWheel()})onBeforeUnmount(() => {if (animationFrameId) {cancelAnimationFrame(animationFrameId)}})watch(() => props.prizes, () => {loadedImages.value = {}loadImages()drawWheel()}, { deep: true })
8. 定义组件样式
通过 CSS 样式定义抽奖转盘组件的外观,包括转盘容器、中心按钮、指针和中奖结果提示框的样式。
.lottery-container {display: flex;flex-direction: column;align-items: center;}.wheel-container {position: relative;width: 350px;height: 350px;margin: 0 auto;}canvas {width: 100%;height: 100%;border-radius: 50%;box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);}.wheel-center {position: absolute;width: 80px;height: 80px;background: white;border-radius: 50%;top: 50%;left: 50%;transform: translate(-50%, -50%);display: flex;align-items: center;justify-content: center;cursor: pointer;z-index: 10;box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);border: 2px solid #e74c3c;}.start-btn {font-size: 16px;font-weight: bold;color: #e74c3c;text-align: center;user-select: none;}.pointer {position: absolute;width: 40px;height: 40px;top: -20px;left: 50%;transform: translateX(-50%);z-index: 10;}.pointer::before {content: '';position: absolute;border-width: 15px;border-style: solid;border-color: transparent transparent #e74c3c transparent;top: 0;left:7px;}.result-modal {position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);background: white;padding: 30px;border-radius: 10px;box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);z-index: 100;text-align: center;}.result-modal h3 {margin-top: 0;color: #e74c3c;}.result-modal button {margin-top: 20px;padding: 10px 20px;background: #e74c3c;color: white;border: none;border-radius: 5px;cursor: pointer;font-size: 16px;}
完整代码
<template><div class="lottery-container"><div class="wheel-container"><canvas ref="canvasRef" width="350" height="350"></canvas><div class="wheel-center" @click="startLottery"><div class="start-btn">{{ isRotating ? '抽奖中...' : '开始抽奖' }}</div></div><div class="pointer"></div></div><div v-if="result" class="result-modal"><h3>恭喜您获得: {{ result.name }}</h3><button @click="result = null">确定</button></div></div>
</template><script setup>
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'const props = defineProps({prizes: {type: Array,required: true,validator: (value) => {return value.length >= 2 && value.every(item => item.name && typeof item.probability === 'number' && item.probability >= 0)}},duration: {type: Number,default: 5000},colors: {type: Array,default: () => ['#FF5252', '#FF4081', '#E040FB', '#7C4DFF','#536DFE', '#448AFF', '#40C4FF', '#18FFFF','#64FFDA', '#69F0AE', '#B2FF59', '#EEFF41','#FFFF00', '#FFD740', '#FFAB40', '#FF6E40']}
})const emit = defineEmits(['start', 'end'])const canvasRef = ref(null)
const isRotating = ref(false)
const rotationAngle = ref(0)
const selectedPrizeIndex = ref(-1)
const result = ref(null)
const loadedImages = ref({})
let animationFrameId = null
let startTime = 0// 预加载所有图片
const loadImages = () => {props.prizes.forEach((prize, index) => {if (prize.icon) {const img = new Image()img.src = prize.iconimg.onload = () => {loadedImages.value[index] = imgdrawWheel()}img.onerror = () => {console.error(`图片加载失败: ${prize.icon}`)}}})
}// 绘制转盘
const drawWheel = () => {const canvas = canvasRef.valueif (!canvas) returnconst ctx = canvas.getContext('2d')const centerX = canvas.width / 2const centerY = canvas.height / 2const radius = Math.min(centerX, centerY) - 10const arc = Math.PI * 2 / props.prizes.lengthctx.clearRect(0, 0, canvas.width, canvas.height)// 绘制扇形props.prizes.forEach((prize, index) => {const startAngle = index * arc + rotationAngle.valueconst endAngle = (index + 1) * arc + rotationAngle.valuectx.beginPath()ctx.fillStyle = props.colors[index % props.colors.length]ctx.moveTo(centerX, centerY)ctx.arc(centerX, centerY, radius, startAngle, endAngle)ctx.closePath()ctx.fill()// 绘制文字和图标ctx.save()ctx.translate(centerX, centerY)ctx.rotate(startAngle + arc / 2)// 绘制文字ctx.fillStyle = '#fff'ctx.font = 'bold 14px Arial'ctx.textAlign = 'center'ctx.fillText(prize.name, radius * 0.7, 5)// 绘制图标if (loadedImages.value[index]) {const img = loadedImages.value[index]ctx.drawImage(img, radius * 0.5 - 15, -15, 30, 30)} else if (prize.icon) {ctx.fillStyle = 'rgba(255,255,255,0.7)'ctx.fillRect(radius * 0.5 - 15, -15, 30, 30)}ctx.restore()})
}// 开始抽奖
const startLottery = () => {if (isRotating.value) returnemit('start')isRotating.value = trueresult.value = nullselectedPrizeIndex.value = selectPrizeByProbability()startTime = performance.now()animateRotation()
}// 动画函数
const animateRotation = () => {const now = performance.now()const elapsed = now - startTimeconst progress = Math.min(elapsed / props.duration, 1)// 缓动函数 - 先快后慢const easeOut = 1 - Math.pow(1 - progress, 4)// 计算旋转角度const anglePerItem = Math.PI * 2 / props.prizes.lengthconst fullCircles = 5 // 完整旋转圈数// 关键修正:减去Math.PI/2(90度)来校准初始位置const targetAngle = fullCircles * Math.PI * 2 + (Math.PI * 2 - anglePerItem * selectedPrizeIndex.value) - (anglePerItem / 2) - Math.PI / 2rotationAngle.value = easeOut * targetAngledrawWheel()if (progress < 1) {animationFrameId = requestAnimationFrame(animateRotation)} else {// 确保最终角度精确对准奖项中心rotationAngle.value = fullCircles * Math.PI * 2 + (Math.PI * 2 - anglePerItem * selectedPrizeIndex.value) - (anglePerItem / 2) - Math.PI / 2drawWheel()isRotating.value = falseemit('end', props.prizes[selectedPrizeIndex.value])result.value = props.prizes[selectedPrizeIndex.value]}
}// 根据概率选择奖项
const selectPrizeByProbability = () => {const totalProbability = props.prizes.reduce((sum, prize) => sum + prize.probability, 0)const random = Math.random() * totalProbabilitylet currentSum = 0for (let i = 0; i < props.prizes.length; i++) {currentSum += props.prizes[i].probabilityif (random <= currentSum) {return i}}return 0
}// 初始化
onMounted(() => {loadImages()drawWheel()
})onBeforeUnmount(() => {if (animationFrameId) {cancelAnimationFrame(animationFrameId)}
})watch(() => props.prizes, () => {loadedImages.value = {}loadImages()drawWheel()
}, { deep: true })
</script><style scoped>
.lottery-container {display: flex;flex-direction: column;align-items: center;
}.wheel-container {position: relative;width: 350px;height: 350px;margin: 0 auto;
}canvas {width: 100%;height: 100%;border-radius: 50%;box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
}.wheel-center {position: absolute;width: 80px;height: 80px;background: white;border-radius: 50%;top: 50%;left: 50%;transform: translate(-50%, -50%);display: flex;align-items: center;justify-content: center;cursor: pointer;z-index: 10;box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);border: 2px solid #e74c3c;
}.start-btn {font-size: 16px;font-weight: bold;color: #e74c3c;text-align: center;user-select: none;
}.pointer {position: absolute;width: 40px;height: 40px;top: -20px;left: 50%;transform: translateX(-50%);z-index: 10;
}.pointer::before {content: '';position: absolute;border-width: 15px;border-style: solid;border-color: transparent transparent #e74c3c transparent;top: 0;left:7px;
}.result-modal {position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);background: white;padding: 30px;border-radius: 10px;box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);z-index: 100;text-align: center;
}.result-modal h3 {margin-top: 0;color: #e74c3c;
}.result-modal button {margin-top: 20px;padding: 10px 20px;background: #e74c3c;color: white;border: none;border-radius: 5px;cursor: pointer;font-size: 16px;
}
</style>
在父组件中prizes的结构为:
[{ name: '一等奖', probability: 5, icon: './public/icons/jiang.png' },{ name: '二等奖', probability: 10, icon: './public/icons/jiang.png' },{ name: '三等奖', probability: 15, icon: './public/icons/jiang.png' },{ name: '四等奖', probability: 20, icon: './public/icons/jiang.png' },{ name: '五等奖', probability: 25, icon: './public/icons/jiang.png' },{ name: '谢谢参与', probability: 25 }]
相关文章:
Vue3 中用 canvas 封装抽奖转盘组件:设定中奖概率及奖项图标和名称
在 Web 应用开发中,抽奖功能是提升用户参与度的常用手段。使用 Vue3 结合 canvas 技术,我们可以轻松实现一个高度自定义的抽奖转盘组件,不仅能设定中奖概率,还能灵活配置奖项图标和名称。本文将详细介绍该组件的实现原理、步骤&am…...
vue3+vite+AI大模型实现谷歌插件-web诊断
vue3viteAI大模型实现谷歌插件-web诊断 一、前言二、实现思路1、功模块构图2、数据交互图 三、技术栈简介1、Web端2、服务端 四、主要功能实现1、Web端【1】谷歌插件vue全局配置文件【2】加载web诊断工具至当前页面【3】全局捕获异常错误 2、Server端【1】websock管理模块【2】…...
高频PCB设计如何选择PCB层数?
以四层板为例,可以第一层和第二层画信号,作为信号层。 第三层可以走电源,然后第四层走GND 但是更可以第一层和第三层画信号。第二层可以走电源,然后第四层走GND 用中间的电源层以及地层可以起到屏蔽的作用,有效降低寄…...
第100+40步 ChatGPT学习:R语言实现多轮建模
回顾一下什么叫多轮建模: 要综合判断一个模型好不好,一次随机抽样是不行的,得多次抽样建模,看看整体的性能如何才行(特别是对于这种小训练集)。 所以我的思路是,随机抽取训练集和验证集2000次…...
DolphinScheduler-3.2.0集群部署教程
详见: DolphinScheduler-3.2.0集群部署教程Centos7 DolphinScheduler集群部署...
如何设计Kafka的高可用跨机房容灾方案?(需要实战,未实战,纯理论)
1. 双活多中心架构设计 startuml 机房A <--> [Kafka Cluster A] : 万兆光纤 机房B <--> [Kafka Cluster B] : 专线网络 机房C <--> [Kafka Cluster C] : VPN隧道[Kafka Cluster A] <-.-> [Kafka Cluster B] : MirrorMaker2双向镜像 [Kafka Cluster B]…...
[人机交互]协作与通信的设计
零.要点 – 解释协作与通信的含义 – 描述人们在协作与通信中使用的社会机制的主要类型 – 概述存在的各种群件系统 – 讨论学科研究和与社交相关的理论,对设计的启示 一.解释协作与通信的含义 1.1什么是通信 通信是个体之间的信息交换的过程 – 按照所 交换信息的…...
LXwhat-嘉立创
一 电路板简介 什么是PCB? 印刷电路板 什么是SMT? 表面贴装技术 有关电路板的几个专业名词 覆铜腐蚀走线多层板 为什么要画电路板? 杜邦线:接线杂乱、虚接、有可能短路洞洞板:考验焊功(虚焊)、异型元器件不适配自己画板:整齐有序、适配异型元器件、紧凑优雅、有成就感(输…...
决 策 树
1 决策树模型 假如你正在运营一家猫咪领养中心,并拥有一些特征数据,你想训练一个分类器来快速判断一只动物是否为猫。这里有十个训练样本,有关于动物耳朵形状、面部形状、是否有胡须的特征,你想要预测这种动物是否为猫࿱…...
ts axios中报 Property ‘code‘ does not exist on type ‘AxiosResponse<any, any>‘
ts语法有严格的格式,如果我们在处理响应数据时,出现了axios响应中非默认字段,就会出现标题那样的警告,我们可以通过创建axios.dt.ts解决这个问题 下面是我在开发中遇到的警告,code并不是axios默认返回的字段࿰…...
【AI】用AI将文档、文字一键生成PPT的方法(百度的自由画布版)
前提: 最近看了个书,周末要参加读书会,要分享这本书的内容。一般来说,我都是写好了内容文档,然后在网上找一些模板套上去。 最近发现,有些网站已经可以按照文档,自动生成PPT模板了,里…...
爬虫技术-利用Python和Selenium批量下载动态渲染网页中的标准文本文件
近日工作需要整理信息安全的各项标准文件,这些文件通常发布在在官方网站,供社会各界下载和参考。 这些页面中,标准文本文件常以Word(.doc/.docx)或PDF格式提供下载。由于文件数量庞大,手动逐条点击下载效率…...
CUDA编程 - 如何在 GPU 上使用 C++ 函数重载 - cppOverload
这里写目录标题 一、完整代码与例程目的二、代码拆解与复用 2.1、函数重载: 2.2、函数指针声明: 2.3、函数指针赋值与内核启动: 2.4、CUDA API调用:2.4.1、cudaFuncSetCacheConfig:2.4.2、cud…...
AI教你学VUE——Gemini版
前端开发学习路线图 (针对编程新手,主攻 Vue 框架) 总原则:先夯实基础,再深入框架。 想象一下建房子,地基不牢,上面的高楼(框架)是盖不起来的。HTML、CSS、JavaScript 就是前端的地基。 阶段一…...
力扣热题100,力扣49.字母异位词分组力扣128.最长连续序列力扣.盛水最多的容器力扣42.接雨水(单调栈)
目录 力扣49.字母异位词分组 力扣128.最长连续序列 力扣.盛水最多的容器 力扣42.接雨水(单调栈) 1.包的命名规范: java的命名规范 全部采用小写 结尾不能加负数 声明包: 位置必须在首行 类: 字母数字下划线,美元符号 不能数字开头 不能有中文 不能以关键字命名 区…...
react naive 网络框架源码解析
本文取 react native 两个区别很大的版本做分析(0.76.5、0.53.3) 一、0.76.5 版fetch 全流程排查 1、JS 端的实现 随手写一个fetch,点开。 我们这里常用的还是手机端,因此选择 react-native,react-native-windows …...
DID在元宇宙的应用爆发:数字身份资产化与跨平台迁移——解析Decentraland等项目的虚拟身份全链路实现
元宇宙的兴起催生了多维度的数字身份需求,但传统虚拟身份系统受限于中心化架构,面临数据孤岛、身份碎片化、资产归属模糊等核心挑战。本文以Decentraland、The Sandbox、Somnium Space等顶级元宇宙平台为研究对象,探讨去中心化身份࿰…...
MySQL的内置函数与复杂查询
目录 前言 一、聚合函数 1.1日期函数 1.2字符串函数 1.3数学函数 1.4其它函数 二、关键字周边 2.1关键字的生效顺序 2.2数据源 2.3可以使用聚合函数的关键字 前言 在前面几篇文章中,讲解了有关MySQL数据库、数据库表的创建、数据库表的数据操作等等。本文我…...
mysql中select 1 from的作用
在MySQL中,SELECT 1 FROM ... 是一个常见的SQL写法,通常用于以下场景: 1. 作用与原理 SELECT 1 的本质是返回一个常数值(即数字1),且不依赖表中的实际数据。 它的核心作用是快速验证逻辑条件是否成立&…...
Linux中 du (详解)、 df (详解)和 free(详解)以及它们的区别
目录 du命令 df命令 free命令 du/df/free区别 Tree du命令 功能:用于计算文件或目录所占用的磁盘空间大小。它会递归地遍历指定目录下的所有文件和子目录,统计它们占用的磁盘块数,从而得出占用的空间大小。常用选项: -h&…...
ETL交通行业案例丨某大型铁路运输集团ETL数据集成实践
在广袤的祖国边疆,一条条钢铁动脉承载着区域经济发展的重要使命。某大型铁路运输集团作为区域交通枢纽的运营主体,管辖着横跨多个省、区的铁路网络,运营里程超3000公里,每日承载着数以万计的客货运输任务。随着"数字中国&quo…...
【数据挖掘】Apriori算法
Apriori算法是经典的关联规则挖掘算法,用于从事务型数据库中发现频繁项集和强关联规则,特别常用于购物篮分析等场景。 🧠 核心思想(Apriori原则) 一个项集是频繁的,前提是它的所有子集也必须是频繁的。 即&…...
7.9/Q1,Charls最新文章解读
文章题目:Association between urbanization levels and frailty among middle-aged and older adults in China: evidence from the CHARLS DOI:10.1186/s12916-025-03961-y 中文标题:中国中老年人城市化水平与虚弱程度之间的关联࿱…...
从入门到登峰-嵌入式Tracker定位算法全景之旅 Part 7 |TinyML 定位:深度模型在 MCU 上的部署
Part 7 |TinyML 定位:深度模型在 MCU 上的部署 本章聚焦如何在 ESP32-S3 平台上,通过 TinyML 将深度学习模型应用到定位场景,包括特征提取、模型剪枝与量化、TensorFlow Lite for Microcontrollers 部署,以及在线微调与自适应策略。 一、为什么要用 TinyML? 非线性特征挖…...
Codeforces Round 1023 (Div. 2) ABC
链接 Dashboard - Codeforces Round 1023 (Div. 2) - Codeforces A 将数组a分成两组,使得gcd(b) ! gcd(c) 思路 gcd(a,b) < min(a,b) 求数组a的max,min 如果数组a都一样无解 (即max min 否则有解:让是max的一组&…...
56. 合并区间
给定若干个区间的集合,将重叠的区间合并后,放入一个数组中返回。 具体思路就是按左端点排序后合并区间,因为按左端点排序后,可以确保每次合并都是以最小元素为合并后区间的起始,并且按左端点排序可以方便合并ÿ…...
Docker安装使用
1.Docker简介 Docker是一个开源的应用容器引擎;是一个轻量级容器技术; Docker支持将软件编译成一个镜像;然后在镜像中各种软件做好配置,将镜像发布出去,其他使用者可以直接使用这个镜像; 运行中的这个镜…...
Linux/AndroidOS中进程间的通信线程间的同步 - POSIX IPC
1 什么是POSIX? POSIX(Portable Operating System Interface)即可移植操作系统接口,它是IEEE为要在各种UNIX操作系统上运行软件,而定义API的一系列标准的总称。以下为你展开介绍: 产生背景:在…...
5.2创新架构
一、MoE(Mixture of Experts,混合专家模型) 了解混合专家模型架构,与 Dense 架构相比有什么优劣 是一种提升大模型推理效率和参数利用率的关键技术 核心思想:在模型中增加多个“专家模块”(Experts&#x…...
驱动开发系列57 - Linux Graphics QXL显卡驱动代码分析(四)显示区域更新
一:概述 前面在介绍了显示模式设置(分辨率,刷新率)之后,本文继续分析下,显示区域的绘制,详细看看虚拟机的画面是如何由QXL显卡绘制出来的。 二:相关数据结构介绍 struct qxl_moni…...
疗愈服务预约小程序源码介绍
基于ThinkPHP、FastAdmin和UniApp开发的疗愈服务预约小程序源码,这款小程序在功能设计和用户体验上都表现出色,为疗愈行业提供了一种全新的服务模式。 该小程序源码采用了ThinkPHP作为后端框架,保证了系统的稳定性和高效性。同时,…...
力扣118,1920题解
记录 2525.5.6 题目: 思路: 用一个二维数组dp[numRows][numRows]保存每一次动态规划的结果 1.令dp[0][0]1(第一列) 2.找规律 3.得到如下规律(以下情况均为列数大于1) if(col0){ dp[row][col]1 } else { dp[row][col]dp[row-1][col-1]dp[row-1][col] }…...
电池热管理CFD解决方案,为新能源汽车筑安全防线
在全球能源结构加速转型的大背景下,新能源汽车产业异军突起,成为可持续发展的重要驱动力。而作为新能源汽车 “心脏” 的电池系统,其热管理技术的优劣,直接决定了车辆的安全性、续航里程和使用寿命。电池在充放电过程中会产生大量…...
(一)毛子整洁架构(Domain Layer/Repository Pattern/Result Pattern/Error Pattern)
文章目录 项目地址一、整洁架构概念1.1 各个分层的功能1. Domain核心部件2. Application Layer3. Infrastructure layer3. Presenetation layer1.2 项目数据库二、Domain Layer2.1 Apartments 实体1. Current Value Obj2. Money Value Obj3. Apartment 类2.2 User 实体1. User类…...
XSS ..
Web安全中的XSS攻击详细教学,Xss-Labs靶场通关全教程(建议收藏) - 白小雨 - 博客园跨站脚本攻击(XSS)主要是攻击者通过注入恶意脚本到网页中,当用户访问该页面时,恶意脚本会在用户的浏览器中执行…...
Github Action部署node项目
Github Action部署node项目 个人学习的时候,作为前端感觉这个CICD基本流程还是有必要了解的,这里记录一下Github Action部署node项目的流程,也算是一个学习的过程 首先肯定是要有一个可运行的node项目 编写部署文件 部署文件放置在.githu…...
高频面试题:设计秒杀系统,用Redis+Lua解决超卖
高频面试题:设计秒杀系统,用RedisLua解决超卖 **1. 问题背景****2. 解决方案:Redis Lua****为什么选择Redis Lua?****核心代码逻辑****Java调用示例(Spring Boot)** **3. 方案优势****4. 面试回答话术***…...
2、Kafka Replica机制与ISR、HW、LEO、AR、OSR详解
Kafka 作为分布式高可用消息队列,其副本(Replica)机制是实现高可靠性和数据一致性的核心。本文将系统介绍 Kafka 的 Replica 机制,并详细解释 ISR、HW、LEO、AR、OSR 等关键概念。 一、Kafka Replica机制概述 在分布式系统中&am…...
生成式 AI:从工具革命到智能体觉醒,2025 年的质变与突破
在上海胸科医院的手术室里,一束全息投影正精准勾勒出患者肺部的三维血管模型。主刀医生手持机械臂的瞬间,AI 导航系统已同步完成 200 次路径演算,将毫米级误差控制在 0.3 毫米以内 —— 这个真实发生在 2025 年的临床场景,标志着生…...
安卓基础(拖拽)
当用户长按或拖拽某个视图(如按钮、图片)时,需要提供视觉反馈(即阴影)。这行代码通常在拖拽事件的处理逻辑中,例如: view.setOnLongClickListener(v -> {// 创建拖拽阴影DragShadowBuilder …...
IoTDB磁盘I/O性能监控与优化指南
一、磁盘I/O性能观测核心指标 在现代计算机系统中,磁盘I/O性能对整体系统表现至关重要。为有效监控和优化磁盘I/O性能,需关注以下核心指标: I/O读写延迟:衡量从发起I/O请求到接收响应的时间间隔。IOPS(Input/O…...
java每日精进 5.06【框架之功能权限】
0.概述 0.1 整体架构概述 这个RBAC权限系统基于Spring Security和Token认证机制,主要包含以下核心组件: 用户-角色-菜单的多对多关系模型 基于Token的认证流程 细粒度的权限控制(菜单权限、按钮权限) 灵活的权限配置方式 1…...
静态NAT
实验需求 PC1和PC2通过静态NAT去访问服务器 实验拓扑 图13-1 静态NAT 实验步骤 步骤1:IP地址的配置 PC1的配置 PC2的配置 R1的配置 <Huawei>system-view [Huawei]undo info-center enable [Huawei]sysname R1 [R1]interface g0/0/0 [R1-GigabitEt…...
RabbitMQ-api开发
前言 MQ就是接收并转发消息 核心概念 admin是用户 每个虚拟机上都有多个交换机 快速入门 引入依赖 <dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>5.22.0</version></dependen…...
const MachineTree getMachineTree() ; MachineTree getMachineTree() const; 区别?
这两个函数声明在语法和语义上有明显的区别,它们的用途和行为也不同。让我们逐一分析它们的区别: 1. const MachineTree &getMachineTree(); 这个函数声明表示: 返回类型:const MachineTree &,即返回一个 M…...
使用DevTools工具调试前端页面,便捷脚本,鸿蒙调试webView
参考官方文章 便捷脚本 创建文本,复制修改后缀为bat 建立bat文件 echo off setlocal enabledelayedexpansion:: Initialize port number and PID list set PORT9222 set PID_LIST:: Get the list of all forwarded ports and PIDs for /f "tokens2,5 delims…...
浏览器存储 Cookie,Local Storage和Session Storage
什么是Cookie? 存储容量:一般限制在 4KB 以内。数据有效期:可以设置过期时间,若未设置,则在浏览器关闭时失效。数据共享:在同一域名下,不同页面可以共享cookie数据。并且在每次 HTTP 请求时&am…...
校内周赛题(思维题)
这次周赛的题目没有什么很难的代码实现,基本上都是对思路的把握。 与君共勉🌹 选取x个数,看能不能使得这x个数相加的结果是奇数。 如果x是偶数,他的分布肯定是一个奇数一个偶数若干个两两配对的奇数若干个两两配对的偶数。 如果…...
在 GitLab 中部署Python定时任务
在 GitLab 中部署定时任务(如每天早8点运行Python脚本并存储结果)可以通过 GitLab CI/CD 结合 计划任务(Scheduled Pipelines) 实现。以下是详细步骤和准备工作: 1. 准备工作 (1) 项目结构准备 确保项目包含: Python脚本(如 main.py):执行核心算法逻辑。 结果存储模…...
学习黑客Windows权限体系
已思考 24 秒 借着 Week 2 Day 4 的号角,我们把权限系统这条「双持长枪」摆上擂台:一边是 Windows DACL/icacls,另一边是 Linux sudoers。你将看到二者在授权语法、常见配置漏洞与提权打法上的一一对照——尤其关注 可写服务(Wr…...