移动端浏览器的扫描二维码实现(vue-qrcode-reader与jsQR方式)

1. 实现功能

类似扫一扫的功能,自动识别到画面中的二维码并进行识别,也可以选择从相册中上传。
在这里插入图片描述
在这里插入图片描述

2. 涉及到的一些插件介绍

vue-qrcode-reader
一组用于检测和解码二维码的Vue.js组件

jsQR
一个纯粹的javascript二维码阅读库,该库接收原始图像,并将定位、提取和解析在其中找到的任何二维码。

zxing-wasm
ZXing-C++ WebAssembly 作为带有类型的 ES/CJS 模块。读/写 web、node、bun 和 deno 中的条形码。

2. vite项目配置本地开发使用https访问

安装basicSsl

pnpm i @vitejs/plugin-basic-ssl
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import basicSsl from '@vitejs/plugin-basic-ssl'
 
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(), basicSsl()],
  server: {
    host: '0.0.0.0',
    https: true
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  }
})

3. 方式一:vue-qrcode-reader实现

npm install vue-qrcode-reader

🔍vue-qrcode-reader Api 介绍

<template>
  <div>
    <qrcode-stream
      class="qrcode-wrap"
      :torch="torch"
      v-memo="[torch]"
      :constraints="selectedConstraints"
      :track="paintBoundingBox"
      @error="onError"
      @detect="onDetect"
      @camera-on="onCameraReady"
    >
      <div v-if="isSupportTorch" class="torch-wrap">
        <div class="torch" @click="() => (torch = !torch)">
          <div class="flash-light" v-if="torch">
            <MdiFlashlightOff style="width: 36px; height: 36px" />
          </div>
          <div class="flash-light" v-else>
            <MdiFlashlight style="width: 36px; height: 36px" />
          </div>
          {{ torch ? "关闭闪光灯" : "打开闪光灯" }}
        </div>
      </div>
      <div class="photo-wrap">
        <div class="photo" @click="handleOpenFile">
          <el-icon size="20">
            <PictureFilled />
          </el-icon>
        </div>
        <div class="color-[#fff]">相册</div>
      </div>
    </qrcode-stream>
  </div>
</template>
<script setup lang="ts">
// https://gruhn.github.io/vue-qrcode-reader/api/QrcodeStream.html
import { QrcodeStream } from "vue-qrcode-reader";
import { PictureFilled } from "@element-plus/icons-vue";
import MdiFlashlight from "~icons/mdi/flashlight";
import MdiFlashlightOff from "~icons/mdi/flashlight-off";
import { ElMessage } from "element-plus";
import { fileOpen } from "browser-fs-access";
import _ from "lodash";

const error = ref("");
const cameraIsReady = ref(false);
const isSupportTorch = ref(false); // 是否支持闪光灯
const torch = ref(false); // 闪光灯状态
// 相机配置选项: 'user'|'environment' (默认:environment)
const selectedConstraints = ref({ facingMode: "environment" });

// 检测到二维码后绘制画布类型
function paintBoundingBox(detectedCodes: any, ctx: CanvasRenderingContext2D) {
  for (const detectedCode of detectedCodes) {
    const {
      boundingBox: { x, y, width, height },
    } = detectedCode;

    ctx.lineWidth = 2;
    ctx.strokeStyle = "#007bff";
    // 绘制边框矩形
    ctx.strokeRect(x, y, width, height);
  }
}

async function onCameraReady(capabilities: any) {
  // NOTE: on iOS we can't invoke `enumerateDevices` before the user has given
  // camera access permission. `QrcodeStream` internally takes care of
  // requesting the permissions. The `camera-on` event should guarantee that this
  // has happened.
  try {
    isSupportTorch.value = !!capabilities.torch;
    cameraIsReady.value = true;
    error.value = "";
  } catch (error) {
    onError(error);
    cameraIsReady.value = false;
  }
}
// 错误提示
function onError(err: any) {
  error.value = `[${err.name}]: `;
  if (err.name === "NotAllowedError") {
    error.value += "you need to grant camera access permission";
  } else if (err.name === "NotFoundError") {
    error.value += "no camera on this device";
  } else if (err.name === "NotSupportedError") {
    error.value += "secure context required (HTTPS, localhost)";
  } else if (err.name === "NotReadableError") {
    error.value += "is the camera already in use?";
  } else if (err.name === "OverconstrainedError") {
    error.value += "installed cameras are not suitable";
  } else if (err.name === "StreamApiNotSupportedError") {
    error.value += "Stream API is not supported in this browser";
  } else if (err.name === "InsecureContextError") {
    error.value +=
      "Camera access is only permitted in secure context. Use HTTPS or localhost rather than HTTP.";
  } else {
    error.value += err.message;
  }
  ElMessage.warning(error.value);
}
// 用户摄像头的流后
function onDetect(detectedCodes: any) {
  if (detectedCodes.length > 0) {
    onDecode(detectedCodes[0]?.rawValue);
  }
}

const emit = defineEmits(["on-success"]);

// 解码(交给父组件处理:进行网络请求)
function onDecode(text: string) {
  emit("on-success", text);
}
// 文件转成base64
const processFile = async (file: any) => {
    let reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = (e) => {
      let base64String = e.target?.result as string ||'';
      // 此处可对该base64进行获取赋值传入后端, 如果有直接上传文件的接口就可以直接传文件
      onDecode(base64String)
    };
};
// 打开图片选择
async function handleOpenFile() {
  try {
    const file = await fileOpen({ mimeTypes: ["image/*"] }).catch(() => null);
    if (!file) return;
    // 计算文件的大小
    const fileSizeMb = _.round(file.size / 1024 / 1024, 2);
    // 文件大小不能超过 10MB
    const limitSizeMb = 10;
    if (fileSizeMb > limitSizeMb) {
      return ElMessage.warning(`图片大小限制 ${limitSizeMb}MB`);
    }
    processFile(file)
  } catch (error) {
    console.log(`[log] - handleOpenUploadIcon - error:`, error);
  }
}
</script>
<style scoped>
.qrcode-wrap {
  position: fixed !important;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: 1 !important;
  background: rgba(0, 0, 0, 0.5);
}
.torch-wrap {
  width: 18.75rem;
  height: 12.5rem;
  position: fixed !important;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -30%);
  z-index: 20;
}

.torch {
  position: fixed;
  bottom: -6.25rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  color: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.photo-wrap {
  position: fixed;
  bottom: 2.875rem;
  left: 2.875rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.photo {
  height: 3.125rem;
  width: 3.125rem;
  background-color: rgba(250, 250, 250, 0.8);
  border-radius: 50%;
  display: grid;
  place-items: center;
  cursor: pointer;
}
</style>

browser-fs-access 这个包需要提前下载

pnpm i browser-fs-access

如果需要在离线的环境解析二维码,则需要使用zxing-wasm,在上面文件的js部分添加以下代码:

// 该文件由zxing-wasm项目构建而来
import wasmFile from './zxing_reader.wasm?url';

// !为了离线加载
// https://github.com/gruhn/vue-qrcode-reader/issues/354
setZXingModuleOverrides({
  locateFile: (path: string, prefix: any) => {
    if (path.endsWith('.wasm')) {
      return wasmFile;
    }
    return prefix + path;
  },
});

✨ 该部分完整Demo代码在该文件夹下:Demo (二维码实现之vue-qrcode-reader)

4. 方式二:jsQR实现

🔍Canvas的基本介绍与使用

<template>
  <div>
    <div class="canvasBox">
      <div class="box">
        <div class="line"></div>
        <div class="angle"></div>
      </div>
      <div v-if="isUseTorch" class="box2">
        <div class="track" @click="openTrack">
          <div class="flash-light" v-if="trackStatus">
            <MdiFlashlightOff style="width: 36px; height: 36px" />
          </div>
          <div class="flash-light" v-else>
            <MdiFlashlight style="width: 36px; height: 36px" />
          </div>
          {{ trackStatus ? "关闭闪光灯" : "打开闪光灯" }}
        </div>
      </div>

      <div class="photo-wrap">
        <div class="photo" @click="handleClickFile">
          <el-icon size="20">
            <input
              class="hide_file"
              ref="fileRef"
              type="file"
              accept="image/*;"
              @change="getFile"
            />
            <PictureFilled />
          </el-icon>
        </div>
        <div class="color-[#fff]">相册</div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
// https://github.com/cozmo/jsQR
import jsQR from "jsqr";
import { PictureFilled } from "@element-plus/icons-vue";
import MdiFlashlight from "~icons/mdi/flashlight";
import MdiFlashlightOff from "~icons/mdi/flashlight-off";
import { ElMessage } from "element-plus";
import _ from "lodash";

const props = withDefaults(
  defineProps<{
    // environment 后摄像头  user 前摄像头
    exact?: "environment" | "user";
    //  whole 全屏  half 半屏
    size?: "whole" | "half";
    // 清晰度: fasle 正常  true 高清
    definition?: boolean;
  }>(),
  {
    exact: "environment",
    size: "whole",
    definition: false,
  }
);
const video = ref();
const canvas2d = ref();
const canvasWidth = ref(520);
const canvasHeight = ref(500);
const c = ref();
const track = ref();
const isUseTorch = ref(false);
const trackStatus = ref(false);
const fileRef = ref();

onMounted(() => {
  const windowWidth = window.screen.availWidth;
  const windowHeight = window.screen.availHeight;

  canvasWidth.value = windowWidth;
  canvasHeight.value = windowHeight;

  nextTick(() => {
    video.value = document.createElement("video");
    c.value = document.createElement("canvas");
    c.value.id = "c";
    c.value.width = canvasWidth.value;
    c.value.height = canvasHeight.value;
    c.value.style.width = "100%";
    document.querySelector(".canvasBox")?.append(c.value);
    openScan();
  });
});

onUnmounted(() => {
  closeCamera();
});
// 开始扫描
async function openScan() {
  try {
    let width = canvasHeight.value;
    width = props.size === "whole" ? width : width * 0.5;
    width = props.definition ? width * 1.6 : width;
    let height = canvasWidth.value;
    height = props.definition ? height * 1.6 : height;
    const videoParam = {
      audio: false,
      video: {
        facingMode: { exact: props.exact }, //强制使用摄像头类型
        width,
        height,
      },
    };
    // 获取用户摄像头的视频流
    const stream = await navigator.mediaDevices.getUserMedia(videoParam);
    if (stream) {
      video.value.srcObject = stream;
      video.value.setAttribute("playsinline", true); //内联播放
      video.value.play();
      requestAnimationFrame(tick);
      // 返回所有的媒体内容流的轨道列表
      track.value = stream.getVideoTracks()?.[0];
      setTimeout(() => {
        // 检测摄像头是否支持闪光灯
        isUseTorch.value = track.value.getCapabilities().torch || null;
      }, 500);
    }
  } catch (error) {
    ElMessage.warning("设备不支持,请检查是否允许摄像头权限!");
    console.log("获取本地设备(摄像头)---失败", error);
  }
}
function closeCamera() {
  if (video.value.srcObject) {
    video.value.srcObject.getTracks().forEach((track: any) => {
      track.stop();
    });
  }
}
function tick() {
  if (video.value.readyState === video.value.HAVE_ENOUGH_DATA) {
    canvasHeight.value = video.value.videoHeight;
    canvasWidth.value = video.value.videoWidth;
    c.value.width = canvasWidth.value;
    c.value.height = canvasHeight.value;
    if (canvas2d.value === undefined) {
      canvas2d.value = c.value.getContext("2d");
    }

    canvas2d.value.drawImage(
      video.value,
      0,
      0,
      canvasWidth.value,
      canvasHeight.value
    );

    const imageData = canvas2d.value.getImageData(
      0,
      0,
      canvasWidth.value,
      canvasHeight.value
    );
    // 解析二维码数据
    const code = jsQR(imageData.data, imageData.width, imageData.height, {
      inversionAttempts: "dontInvert",
    });

    if (!_.isEmpty(code)) {
      drawLine(
        code.location.topLeftCorner,
        code.location.topRightCorner,
        "#FF3B58"
      );
      drawLine(
        code.location.topRightCorner,
        code.location.bottomRightCorner,
        "#FF3B58"
      );
      drawLine(
        code.location.bottomRightCorner,
        code.location.bottomLeftCorner,
        "#FF3B58"
      );
      drawLine(
        code.location.bottomLeftCorner,
        code.location.topLeftCorner,
        "#FF3B58"
      );
      if (code.data) {
        getData(code.data);
      }
    }
  }
  requestAnimationFrame(tick);
}
function drawLine(begin: any, end: any, color: string) {
  canvas2d.value.beginPath();
  canvas2d.value.moveTo(begin.x, begin.y);
  canvas2d.value.lineTo(end.x, end.y);
  canvas2d.value.lineWidth = 4;
  canvas2d.value.strokeStyle = color;
  canvas2d.value.stroke();
}
const emit = defineEmits(["on-success"]);

function getData(data: string) {
  emit("on-success", data);
  closeCamera();
}

function openTrack() {
  trackStatus.value = !trackStatus.value;
  track.value.applyConstraints({
    advanced: [{ torch: trackStatus.value }],
  });
}
const handleClickFile = () => {
  fileRef.value.click();
};
const getFile = (e: any) => {
  const file = e.target.files[0];
  emit("on-success", file);
  closeCamera();
};
</script>

<style scoped>
.flash-light {
  display: grid;
  place-content: center;
  margin-bottom: 6px;
}

.photo-wrap {
  position: fixed;
  bottom: 2.875rem;
  left: 2.875rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.photo {
  height: 3.125rem;
  width: 3.125rem;
  background-color: rgba(250, 250, 250, 0.8);
  border-radius: 50%;
  display: grid;
  place-items: center;
  cursor: pointer;
}

.hide_file {
  display: none;
}

page {
  background-color: #333333;
}

.canvasBox {
  width: 100vw;
  position: relative;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-image: linear-gradient(
      0deg,
      transparent 24%,
      rgba(32, 255, 77, 0.1) 25%,
      rgba(32, 255, 77, 0.1) 26%,
      transparent 27%,
      transparent 74%,
      rgba(32, 255, 77, 0.1) 75%,
      rgba(32, 255, 77, 0.1) 76%,
      transparent 77%,
      transparent
    ),
    linear-gradient(
      90deg,
      transparent 24%,
      rgba(32, 255, 77, 0.1) 25%,
      rgba(32, 255, 77, 0.1) 26%,
      transparent 27%,
      transparent 74%,
      rgba(32, 255, 77, 0.1) 75%,
      rgba(32, 255, 77, 0.1) 76%,
      transparent 77%,
      transparent
    );
  background-size: 3rem 3rem;
  background-position: -1rem -1rem;
  z-index: 10;
  background-color: #1110;
}

.box {
  width: 11.9375rem;
  height: 11.9375rem;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -80%);
  overflow: hidden;
  border: 0.1rem solid rgba(0, 255, 51, 0.2);
  z-index: 11;
}

.line {
  height: calc(100% - 2px);
  width: 100%;
  background: linear-gradient(180deg, rgba(0, 255, 51, 0) 43%, #00ff33 211%);
  border-bottom: 3px solid #00ff33;
  transform: translateY(-100%);
  animation: radar-beam 2s infinite alternate;
  animation-timing-function: cubic-bezier(0.53, 0, 0.43, 0.99);
  animation-delay: 1.4s;
}

.box:after,
.box:before,
.angle:after,
.angle:before {
  content: "";
  display: block;
  position: absolute;
  width: 3vw;
  height: 3vw;
  z-index: 12;
  border: 0.2rem solid transparent;
}

.box:after,
.box:before {
  top: 0;
  border-top-color: #00ff33;
}

.angle:after,
.angle:before {
  bottom: 0;
  border-bottom-color: #00ff33;
}

.box:before,
.angle:before {
  left: 0;
  border-left-color: #00ff33;
}

.box:after,
.angle:after {
  right: 0;
  border-right-color: #00ff33;
}

@keyframes radar-beam {
  0% {
    transform: translateY(-100%);
  }

  100% {
    transform: translateY(0);
  }
}

.box2 {
  width: 18.75rem;
  height: 12.5rem;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -80%);
  z-index: 20;
}

.track {
  position: absolute;
  bottom: -6.25rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  color: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
}
</style>

✨该部分完整Demo代码在该文件夹下:Demo (二维码实现之jsQR)

5. 总结

方式一结合vue-qrcode-reader的封装性更强且识别二维码速度更快。
方式二接近了vue-qrcode-reader的底层实现过程。
实际项目开发中更推荐方式一,兼容性与稳定性会更好些

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用 3D 图形 API 在 C# 中将 PLY 转换为 OBJ

OBJ和PLY是一些广泛使用的 3D 文件格式&#xff0c;易于编写和读取。这篇博文演示了如何以编程方式在 C# 中将 PLY 转换为 OBJ。此外&#xff0c;它还介绍了一种用于 3D 文件格式转换的在线3D 转换器。是的&#xff0c;Aspose.3D for .NET为程序员和非程序员提供了此功能来执行…

MTK烧录USB驱动下载

下载链接 https://www.catalog.update.microsoft.com/Search.aspx?qMediaTek%20USB%20Port 驱动安装教程 https://miuiver.com/install-official-mediatek-driver/

交友系统定制版源码 相亲交友小程序源码全开源可二开 打造独特的社交交友系统

交友系统源码的实现涉及到多个方面&#xff0c;包括前端页面设计、后端逻辑处理、数据库设计以及用户交互等。以下是一个简单的交友系统源码实现的基本框架和关键步骤: 1.数据库设计:用户表:存储用户基本信息&#xff0c;如用户ID、用户名、密码、头像、性别、年龄、地理位置…

SpringCloud 前端-网关-微服务-微服务间实现信息共享传递

目录 1 网关获取用户校验信息并保存至请求头&#xff08;前端-网关&#xff09; 2 微服务获取网关中的用户校验信息&#xff08;网关-微服务&#xff09; 2.1 一般的做法是在公共的module中添加&#xff0c;此处示例为common 公共配置module中添加 2.2 定义拦截器 2.3 定义…

什么是微控制器中的欠压复位?如何防止误断电

微控制器的“掉电”是指电源电压部分暂时降低到可靠运行所需的水平以下。许多微控制器都有一个保护电路&#xff0c;可以检测电源电压何时低于此水平&#xff0c;并将设备置于复位状态&#xff0c;以确保电源恢复时正确启动。此操作称为“欠压复位”或 BOR。类似的功能称为低电…

数据不归路?文件清理的后悔药,2个文件恢复技巧

手机已成为我们生活中不可或缺的重要工具&#xff0c;它不仅仅是一个通讯设备&#xff0c;更是我们存储个人信息、工作文件、照片和视频等宝贵资料的仓库。然而&#xff0c;生活中的意外总是难以预料&#xff0c;有时候我们可能会不小心删除重要的文件&#xff0c;或者因为手机…

JVS规则引擎实战:如何轻松接入本地数据库数据

在当今数据驱动的时代&#xff0c;有效地接入和利用各种数据源是企业和组织实现智能化、自动化决策的关键。JVS-RULES通过支持多种数据形态&#xff0c;为用户提供了一个统一的数据接入平台&#xff0c;使不同来源的数据能够被整合并用于规则判断。接下来我给大家详细介绍如何通…

鸿蒙轻内核M核源码分析系列二十 Newlib C

LiteOS-M内核LibC实现有2种&#xff0c;可以根据需求进行二选一&#xff0c;分别是musl libC和newlibc。本文先学习下Newlib C的实现代码。文中所涉及的源码&#xff0c;均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m 获取。 使用Musl C库的时候&#xff0c…

CSS之块浮动

在盒子模型的基础上就可以对网页进行设计 不知道盒子模型的可以看前面关于盒子模型的内容 而普通的网页设计具有一定的原始规律,这个原始规律就是文档流 文档流 标签在网页二维平面内默认的一种排序方式,块级标签不管怎么设置都会占一行,而同一行不能放置两个块级标签 行级…

Ubuntu编译虚幻引擎工程

前言 最近研究了一下在ubuntu编译虚幻引擎&#xff0c;发现确实做得很好&#xff0c;编译非常简单&#xff0c;这里记录一下。 下载虚幻引擎源码 源码下载地址如下https://www.unrealengine.com/zh-CN/linux 选择合适的版本即可&#xff0c;我这里选择的是UE5.1 安装dotnet驱动…

前端JS必用工具【js-tool-big-box】学习,获取当前浏览器向上滚动还是向下滚动,获取当前距离顶部和底部的距离

这一小节&#xff0c;我们说一下 js-tool-big-box 添加的最新工具方法&#xff0c;在日常前端开发工作中&#xff0c;如果网页很长&#xff0c;我们就需要获取当前浏览器是在向上滚动&#xff0c;还是向下滚动。如果向上滚动&#xff0c;滚动到0的时候呢&#xff0c;需要做一些…

金智易表通流程设置的若干问题

1、审批节点的审批人取应用权限组&#xff0c;权限组内任一人审批即可通过 在流程节点的主要配置环节&#xff0c;选择候选组 二、已审菜单要求看到自己审过的也能看到别人审过的&#xff0c;即能看到所有已审的记录 管理设置中取消按钮对流程的依赖&#xff0c;不根据流程审批…

二叉树最大深度

leetcode- 104-二叉树的最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1a; 输入&…

阿一网络安全学院来向你科普关于企业安全服务

一、四大服务体系 1、可管理安全服务 在提供传统安全产品及安全服务的基础上&#xff0c;逐步开展安全运营&#xff0c;用开放的安全平台连接卓越的产品和服务&#xff0c;洞察安全态势&#xff0c;为企业级用户提供小时级的闭环安全保障。 2、安全咨询服务 为客户进行全方…

苹果AI一夜颠覆所有,Siri史诗级进化,内挂GPT-4o

苹果AI一夜颠覆所有&#xff0c;Siri史诗级进化&#xff0c;内挂GPT-4o 刚刚&#xff0c;苹果AI&#xff0c;正式交卷&#xff01; 今天&#xff0c;苹果构建了一个全新AI帝国——个人化智能系统Apple Intelligence诞生&#xff0c;智能助手Siri迎来诞生13年以来的史诗级进化…

2024年【G2电站锅炉司炉】报名考试及G2电站锅炉司炉考试报名

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 G2电站锅炉司炉报名考试是安全生产模拟考试一点通生成的&#xff0c;G2电站锅炉司炉证模拟考试题库是根据G2电站锅炉司炉最新版教材汇编出G2电站锅炉司炉仿真模拟考试。2024年【G2电站锅炉司炉】报名考试及G2电站锅炉…

MCU为什么上电不启动

相信很多朋友们都遇到过&#xff0c;自信满满的将程序下载到板子上&#xff0c;发现MCU居然没启动。 那这个现象可能有很多问题会导致&#xff0c;让我们来看看会有哪些原因。 1、BOOT引脚电平不对&#xff1a; 在GD32 MCU上&#xff0c;BOOT引脚决定了MCU的启动方式&#x…

Elastic Search 8.14:更快且更具成本效益的向量搜索,使用 retrievers 和重新排序提升相关性,RAG 和开发工具

作者&#xff1a;来自 Elastic Yaru Lin, Ranjana Devaji 我们致力于突破搜索开发的界限&#xff0c;并专注于为搜索构建者提供强大的工具。通过我们的最新更新&#xff0c;Elastic 对于处理以向量表示的大量数据的客户来说变得更加强大。这些增强功能保证了更快的速度、降低的…

Nvidia/算能 +FPGA+AI大算力边缘计算盒子:AI智能监控 用于沙滩救援

以色列的一个团队在人工智能领域取得的成果引起了轰动。 今天他们取得的成果源于多年前的一个想法。Netanel Eliav 和 Adam Bismut 是校园时代的旧伙伴&#xff0c;当时他们想要解决一个可以改变世界的问题&#xff0c;由此引出这样一个想法&#xff1a;溺水的 Bismut 漂流到死…

【CT】LeetCode手撕—25. K 个一组翻转链表

目录 题目1-思路2- 实现⭐25. K 个一组翻转链表——题解思路 3- ACM实现 题目 原题连接&#xff1a;25. K 个一组翻转链表 1-思路 1. dummyHead&#xff1a;设置虚拟头结点&#xff0c;通过虚拟头结点保证每个结点的地位相同2. 定位 pre 和 end 拆链&#xff1a;借助 pre 、s…