聊一聊cropper.js

2023-05-16

最近的项目中有一个纯前端实现的功能困扰了我好久,就是用户上传图片以后需要用户进入图片裁剪页并完成上传的功能,一开始我是打算自己去用canvas去写这样一个页面的,但是项目开发周期短,任务紧,所以我就在网上各种扒资源,还真让我找到了这么一款神仙插件——cropper.js。

cropper.js

jQuery.cropper是一款使用简单且功能强大的图片剪裁jQuery插件。该图片剪裁插件支持图片放大缩小,支持图片旋转,支持触摸屏设备,支持canvas,并且支持跨浏览器使用。
在这里插入图片描述

官方地址:http://fengyuanchen.github.io/cropper/
官方文档:https://github.com/fengyuanchen/cropper/blob/master/README.md

接下来就让我们一起来聊聊这个插件:
在这里插入图片描述
这个是官方上给出的一张图,从图上不难看出这个插件实现的原理。
通过canvas实现图片裁剪,最后在通过canvas获取裁剪区域的图片base64串。
cropper提供了大量的参数、方法和事件供图片的剪裁操作。

安装

可以通过Bower或NPM来安装该插件。

bower install cropper
npm install cropper

使用方法

使用该图片剪裁插件首先要引入必要的js和css文件。

<script src="/path/to/jquery.js"></script><!-- jQuery is required -->
<link  href="/path/to/cropper.css" rel="stylesheet">
<script src="/path/to/cropper.js"></script>       

可以将图片或canvas直接包裹到一个块级元素中。

<!-- Wrap the image or canvas with a block element -->
<div class="container">
    <img src="picture.jpg">
</div>

可以使用$.fn.cropper方法来初始化该图片剪裁插件。

$('.container > img').cropper({
    aspectRatio: 16 / 9,
    crop: function(data) {
        // Output the result data for cropping image.
    }
});   

注意:剪裁区域的尺寸继承自图片的父容器(包裹容器),所以要确保包裹图片的是一个可见的块级元素。输出的剪裁数据基于原始的图片尺寸,你可以使用这些数据直接剪裁图片。如果你要使用跨源图片来作为剪裁图片,请确保你的浏览器支持HTML5 CORS settings attributes,并且你的图片服务器支持Access-Control-Allow-Origin属性。

配置参数

cropper.js提供了一些可配置的参数用来实现一些裁剪框和图片的功能

可以通过$().cropper(options)方法来设置参数。如果你想改变全局默认参数,可以使用$.fn.cropper.setDefaults(options)方法。 
aspectRatio:类型:Number,默认值NaN。设置剪裁容器的比例。 
crop:类型:Function,默认值null。当改变剪裁容器或图片时的事件函数。 
preview:类型:String(jQuery选择器),默认值”。添加额外的元素(容器)的预览。 

现在我粘出我的代码,这里是一些参数的设置:

//创建dom
      this.createElement()
      //初始化裁剪对象
      this.cropper = new Cropper(this.preview, {
        aspectRatio: opt.aspectWithRatio / opt.aspectHeightRatio, // 裁剪框比例 默认NaN
        // aspectRatio: 1/1,
        autoCropArea: opt.autoCropArea || 0.8, // 默认值0.8(图片的80%)。0-1之间的数值,定义自动剪裁框的大小。
        viewMode: 2, //显示模式
        guides: true, // 类型:Boolean,默认值true。是否在剪裁框上显示虚线。
        cropBoxResizable: true, //是否通过拖动来调整剪裁框的大小
        cropBoxMovable: true, //是否通过拖拽来移动剪裁框。
        dragCrop: false,
        dragMode: 'move', //‘crop’: 可以产生一个新的裁剪框3 ‘move’: 只可以移动3 ‘none’: 什么也不处理
        center: true, // 默认true  是否显示裁剪框 中间的+
        zoomable: true, //是否允许放大图像。
        zoomOnTouch: true, //是否可以通过拖动触摸来放大图像。
        zoomOnWheel: true, // 默认 true 是否允许鼠标滚轴 缩放图片
        rotatable: true, // 类型:Boolean,默认值true。是否允许旋转图片
        scalable: true,
        background: false, // 类型:Boolean,默认值true。是否在容器上显示网格背景
        checkOrientation: false,
        checkCrossOrigin: false, // 类型:Boolean,默认值true。默认情况下,插件会检测图片的源,如果是跨域图片,图片元素会被添加crossOrigin class,并会为图片的url添加一个时间戳来使getCroppedCanvas变为可用。
        toggleDragModeOnDblclick: true, // 认true .是否允许 拖动模式 “crop” 跟“move” 的切换状态。。即当点下为crop 模式,如果未松开拖动这时就是“move”模式。放开后又为“crop”模式
        ready: function () {
          if (opt.aspectRatio == 'Free') {
            let cropBox = _this.cropper.cropBox
            cropBox.querySelector('span.cropper-view-box').style.outline =
              'none'
            _this.cropper.disable()
          }
        },
      })

这些参数的说明我都一一的标在了后面,其中需要特别注意的是checkCrossOrigin这个参数,这个参数会检测图片的源,并且会调用一个Origin的接口,在项目中就很容易造成跨域的错误,这个参数的默认值是true,当时我用这个的时候就报了一堆length的错误,搞得我头大。。。

https://blog.csdn.net/qq727013465/article/details/51823231,当时我就是看了这篇文章,这篇文章上有详细的参数说明并且还有详细的使用代码。

下面这里有详细代码展示,仅供大家参考一下,

<!-- 这个图片剪裁插件,兼容ios与安卓 --> 
<template>
  <div>
    <input
      class="upload-img"
      type="file"
      name="file"
      ref="files"
      accept="image/*"
      @change="change($event)"
    />
  </div>
</template>
<script>
import Cropper from 'cropperjs'
// import Exif from 'exif-js'
export default {
  name: 'uploadpage',
  components: {},
  props: {
  },
  data() {
    return {
      widthRate: '650',
      heightRate: '800',
      rotateNum:0,//旋转的角度
    }
  },
  methods: {
    change(event) {
      this.clip(event, {
        aspectWithRatio: Number(this.widthRate),
        aspectHeightRatio: Number(this.heightRate),
      })
    },
    //初始化方法
    initilize(opt) {
      let _this = this
      this.options = opt
      //创建dom
      this.createElement()
      //初始化裁剪对象
      this.cropper = new Cropper(this.preview, {
        aspectRatio: opt.aspectWithRatio / opt.aspectHeightRatio, // 裁剪框比例 默认NaN
        // aspectRatio: 1/1,
        autoCropArea: opt.autoCropArea || 0.8, // 默认值0.8(图片的80%)。0-1之间的数值,定义自动剪裁框的大小。
        viewMode: 2, //显示模式
        guides: true, // 类型:Boolean,默认值true。是否在剪裁框上显示虚线。
        cropBoxResizable: true, //是否通过拖动来调整剪裁框的大小
        cropBoxMovable: true, //是否通过拖拽来移动剪裁框。
        dragCrop: false,
        dragMode: 'move', //‘crop’: 可以产生一个新的裁剪框3 ‘move’: 只可以移动3 ‘none’: 什么也不处理
        center: true, // 默认true  是否显示裁剪框 中间的+
        zoomable: true, //是否允许放大图像。
        zoomOnTouch: true, //是否可以通过拖动触摸来放大图像。
        zoomOnWheel: true, // 默认 true 是否允许鼠标滚轴 缩放图片
        rotatable: true, // 类型:Boolean,默认值true。是否允许旋转图片
        scalable: true,
        background: false, // 类型:Boolean,默认值true。是否在容器上显示网格背景
        checkOrientation: false,
        checkCrossOrigin: false, // 类型:Boolean,默认值true。默认情况下,插件会检测图片的源,如果是跨域图片,图片元素会被添加crossOrigin class,并会为图片的url添加一个时间戳来使getCroppedCanvas变为可用。
        toggleDragModeOnDblclick: true, //true .是否允许 拖动模式 “crop” 跟“move” 的切换状态。。即当点下为crop 模式,如果未松开拖动这时就是“move”模式。放开后又为“crop”模式
        ready: function () {
          if (opt.aspectRatio == 'Free') {
            let cropBox = _this.cropper.cropBox
            cropBox.querySelector('span.cropper-view-box').style.outline =
              'none'
            _this.cropper.disable()
          }
        },
      })
    },
    //创建一些必要的DOM,用于图片裁剪
    createElement() {
      //初始化图片为空对象
      this.preview = null
      let str =
        '<div><img id="clip_image" src="../assets/img/nobutton.png"></div><button type="button" id="cancel_clip">取消</button></div><button type="button" id="clip_button">确定</button>'
      str +=
        '<div class="crop_loading"><div class="crop_content"><div class="crop_text">图片修剪中...</div></div></div>'
      str +=
        '<div class="crop_success"><div class="crop_success_text">上传成功</div></div></div>'

      let body = document.getElementsByTagName('body')[0]
      this.reagion = document.createElement('div')
      this.reagion.id = 'clip_container'
      this.reagion.className = 'container'
      this.reagion.innerHTML = str
      //添加创建好的DOM元素
      body.appendChild(this.reagion)
      this.preview = document.getElementById('clip_image')

      //绑定一些方法
      this.initFunction()
    },
    //初始化一些函数绑定
    initFunction() {
      let _this = this
      this.clickBtn = document.getElementById('clip_button')
      this.cancelBtn = document.getElementById('cancel_clip')
      //确定事件
      this.addEvent(this.clickBtn, 'click', function () {
        _this.crop()
      })
      //取消事件
      this.addEvent(this.cancelBtn, 'click', function () {
        _this.destoried()
      })
      //清空input的值
      this.addEvent(this.fileObj, 'click', function () {
        this.value = ''
      })
    },
    //外部接口,用于input['file']对象change时的调用
    clip(e, opt) {
      this.fileObj = e.srcElement
      let files = e.target.files || e.dataTransfer.files
      //   if (!files.length) return false //不是图片直接返回
      //调用初始化方法
      this.initilize(opt)
      //获取图片文件资源
      this.picValue = files[0]
      //调用方法转成url格式
      this.originUrl = this.getObjectURL(this.picValue)
      //每次替换图片要重新得到新的url
      if (this.cropper) {
        this.cropper.replace(this.originUrl)
      }
    },
    //图片转码方法
    getObjectURL(file) {
      let url = null
      if (window.createObjectURL != undefined) {
        // basic
        url = window.createObjectURL(file)
      } else if (window.URL != undefined) {
        // mozilla(firefox)
        url = window.URL.createObjectURL(file)
      } else if (window.webkitURL != undefined) {
        // webkit or chrome
        url = window.webkitURL.createObjectURL(file)
      }
      return url
    },
    //点击确定进行裁剪
    crop() {
      let _this = this
      let image = new Image()
      let croppedCanvas
      let roundedCanvas
      // Crop
      document.querySelector('.crop_loading').style.display = 'block'
      setTimeout(function () {
        croppedCanvas = _this.cropper.getCroppedCanvas()
        // Round
        roundedCanvas = _this.getRoundedCanvas(croppedCanvas)
        let imgData = roundedCanvas.toDataURL()
        image.src = imgData
        //图片上传
        _this.postImg(imgData)
      }, 20)
    },
    //获取裁剪图片资源
    getRoundedCanvas(sourceCanvas) {
      let canvas = document.createElement('canvas')
      let context = canvas.getContext('2d')
      let width = sourceCanvas.width
      let height = sourceCanvas.height
      canvas.width = width
      canvas.height = height
      context.imageSmoothingEnabled = true
      context.drawImage(sourceCanvas, 0, 0, width, height)
      context.globalCompositeOperation = 'destination-in'
      context.beginPath()
      context.rect(0, 0, width, height)
      context.fill()
      return canvas
    },
    //销毁原来的对象
    destoried() {
      //移除事件
      this.removeEvent(this.clickBtn, 'click', null)
      this.removeEvent(this.cancelBtn, 'click', null)
      this.removeEvent(this.fileObj, 'click', null)
      //移除裁剪框
      this.reagion.parentNode.removeChild(this.reagion)

      //销毁裁剪对象
      this.cropper.destroy()
      this.cropper = null
    },
    //图片上传
    postImg(imageData) {
      //this.$emit('callback', imageData)
      //这边写图片的上传
      let _this = this
      _this.destoried()
    },
    //图片旋转
    rotateImg(img, direction, canvas) {
      //最小与最大旋转方向,图片旋转4次后回到原方向
      const min_step = 0
      const max_step = 3
      if (img == null) return
      //img的高度和宽度不能在img元素隐藏后获取,否则会出错
      let height = img.height
      let width = img.width
      let step = 2
      if (step == null) {
        step = min_step
      }
      if (direction == 'right') {
        step++
        //旋转到原位置,即超过最大值
        step > max_step && (step = min_step)
      } else {
        step--
        step < min_step && (step = max_step)
      }
      //旋转角度以弧度值为参数
      let degree = (step * 90 * Math.PI) / 180
      let ctx = canvas.getContext('2d')
      switch (step) {
        case 0:
          canvas.width = width
          canvas.height = height
          ctx.drawImage(img, 0, 0)
          break
        case 1:
          canvas.width = height
          canvas.height = width
          ctx.rotate(degree)
          ctx.drawImage(img, 0, -height)
          break
        case 2:
          canvas.width = width
          canvas.height = height
          ctx.rotate(degree)
          ctx.drawImage(img, -width, -height)
          break
        case 3:
          canvas.width = height
          canvas.height = width
          ctx.rotate(degree)
          ctx.drawImage(img, -width, 0)
          break
      }
    },
    //添加事件
    addEvent(obj, type, fn) {
      if (obj.addEventListener) {
        obj.addEventListener(type, fn, false)
      } else {
        obj.attachEvent('on' + type, fn)
      }
    },
    //移除事件
    removeEvent(obj, type, fn) {
      if (obj.removeEventListener) {
        obj.removeEventListener(type, fn, false)
      } else {
        obj.detachEvent('on' + type, fn)
      }
    },
  },
}
</script>
<style lang="less" scoped>
.button {
  width: 325px;
  height: 50px;
  background: url('../assets/img/button.png');
  background-size: 100% 100%;
  margin: 20px auto;
  position: relative;
}
.nobutton {
  width: 325px;
  height: 50px;
  background: url('../assets/img/nobutton.png');
  background-size: 100% 100%;
  margin: 20px auto;
  position: relative;
}

.upload-img {
  position: absolute;
  width: 80%;
  height: 100%;
  top: 0;
  left: 0;
  opacity: 0;
}
</style>
<style>
#clip_button {
  position: absolute;
  right: 10%;
  bottom: 20px;
  width: 80px;
  height: 40px;
  border: none;
  border-radius: 2px;
  background: #1aad19;
  color: #fff;
}

#cancel_clip {
  position: absolute;
  left: 10%;
  bottom: 20px;
  width: 80px;
  height: 40px;
  border: none;
  border-radius: 2px;
  color: #fff;
  background: #e64340;
}
#cancel_rotate {
  position: absolute;
  left: 40%;
  bottom: 20px;
  width: 80px;
  height: 40px;
  border: none;
  border-radius: 2px;
  color: #fff;
  background: orange;
}
#clip_container.container {
  z-index: 99999;
  position: fixed;
  padding-top: 60px;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 1);
}

#clip_container.container > div {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 50%;
  left: 50%;
  -webkit-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
}

#clip_image {
  max-width: 100%;
}

.cropper-container {
  font-size: 0;
  line-height: 0;
  position: relative;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  direction: ltr;
  -ms-touch-action: none;
  touch-action: none;
}

.crop_loading,
.crop_success {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 9;
}

.crop_loading .crop_content {
  position: absolute;
  top: 50%;
  left: 50%;
  text-align: center;
  background: #000;
  opacity: 0.9;
  height: 66px;
  width: 140px;
  vertical-align: middle;
  color: #fff;
  padding-top: 20px;
  font-size: 16px;
  -webkit-border-radius: 3px;
  border-radius: 3px;
  -webkit-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
}

.crop_loading .crop_content img {
  margin-top: 15px;
  margin-bottom: 10px;
}

.crop_success .crop_success_text {
  position: absolute;
  top: 50%;
  left: 50%;
  text-align: center;
  background: #000;
  opacity: 0.9;
  width: 120px;
  height: 30px;
  color: #fff;
  line-height: 30px;
  font-size: 16px;
  -webkit-border-radius: 3px;
  border-radius: 3px;
  -webkit-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
}

.cropper-container img {
  /* Avoid margin top issue (Occur only when margin-top <= -height) */
  display: block;
  min-width: 0 !important;
  max-width: none !important;
  min-height: 0 !important;
  max-height: none !important;
  width: 100%;
  height: 100%;
  image-orientation: 0deg;
}

.cropper-wrap-box,
.cropper-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-modal {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

.cropper-wrap-box {
  overflow: hidden;
}

.cropper-drag-box {
  opacity: 0;
  background-color: #fff;
}

.cropper-modal {
  opacity: 0.5;
  background-color: #000;
}

.cropper-view-box {
  display: block;
  overflow: hidden;

  width: 100%;
  height: 100%;

  outline: 1px solid #39f;
  outline-color: rgba(51, 153, 255, 0.75);
}

.cropper-dashed {
  position: absolute;

  display: block;

  opacity: 0.5;
  border: 0 dashed #eee;
}

.cropper-dashed.dashed-h {
  top: 33.33333%;
  left: 0;
  width: 100%;
  height: 33.33333%;
  border-top-width: 1px;
  border-bottom-width: 1px;
}

.cropper-dashed.dashed-v {
  top: 0;
  left: 33.33333%;
  width: 33.33333%;
  height: 100%;
  border-right-width: 1px;
  border-left-width: 1px;
}

.cropper-center {
  position: absolute;
  top: 50%;
  left: 50%;

  display: block;

  width: 0;
  height: 0;

  opacity: 0.75;
}

.cropper-center:before,
.cropper-center:after {
  position: absolute;
  display: block;
  content: ' ';
  background-color: #eee;
}

.cropper-center:before {
  top: 0;
  left: -3px;
  width: 7px;
  height: 1px;
}

.cropper-center:after {
  top: -3px;
  left: 0;
  width: 1px;
  height: 7px;
}

.cropper-face,
.cropper-line,
.cropper-point {
  position: absolute;

  display: block;

  width: 100%;
  height: 100%;

  opacity: 0.1;
}

.cropper-face {
  top: 0;
  left: 0;

  background-color: #fff;
}

.cropper-line {
  background-color: #39f;
}

.cropper-line.line-e {
  top: 0;
  right: -3px;
  width: 5px;
  cursor: e-resize;
}

.cropper-line.line-n {
  top: -3px;
  left: 0;
  height: 5px;
  cursor: n-resize;
}

.cropper-line.line-w {
  top: 0;
  left: -3px;
  width: 5px;
  cursor: w-resize;
}

.cropper-line.line-s {
  bottom: -3px;
  left: 0;
  height: 5px;
  cursor: s-resize;
}

.cropper-point {
  width: 5px;
  height: 5px;

  opacity: 0.75;
  background-color: #39f;
}

.cropper-point.point-e {
  top: 50%;
  right: -3px;
  margin-top: -3px;
  cursor: e-resize;
}

.cropper-point.point-n {
  top: -3px;
  left: 50%;
  margin-left: -3px;
  cursor: n-resize;
}

.cropper-point.point-w {
  top: 50%;
  left: -3px;
  margin-top: -3px;
  cursor: w-resize;
}

.cropper-point.point-s {
  bottom: -3px;
  left: 50%;
  margin-left: -3px;
  cursor: s-resize;
}

.cropper-point.point-ne {
  top: -3px;
  right: -3px;
  cursor: ne-resize;
}

.cropper-point.point-nw {
  top: -3px;
  left: -3px;
  cursor: nw-resize;
}

.cropper-point.point-sw {
  bottom: -3px;
  left: -3px;
  cursor: sw-resize;
}

.cropper-point.point-se {
  right: -3px;
  bottom: -3px;
  width: 5px;
  height: 5px;
  cursor: se-resize;
  opacity: 1;
}

@media (min-width: 768px) {
  .cropper-point.point-se {
    width: 15px;
    height: 15px;
  }
}

@media (min-width: 992px) {
  .cropper-point.point-se {
    width: 10px;
    height: 10px;
  }
}

@media (min-width: 1200px) {
  .cropper-point.point-se {
    width: 5px;
    height: 5px;
    opacity: 0.75;
  }
}

.cropper-point.point-se:before {
  position: absolute;
  right: -50%;
  bottom: -50%;
  display: block;
  width: 200%;
  height: 200%;
  content: ' ';
  opacity: 0;
  background-color: #39f;
}

.cropper-invisible {
  opacity: 0;
}

.cropper-bg {
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMzTjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');
}

.cropper-hide {
  position: absolute;
  display: block;
  width: 0;
  height: 0;
}

.cropper-hidden {
  display: none !important;
}

.cropper-move {
  cursor: move;
}

.cropper-crop {
  cursor: crosshair;
}

.cropper-disabled .cropper-drag-box,
.cropper-disabled .cropper-face,
.cropper-disabled .cropper-line,
.cropper-disabled .cropper-point {
  cursor: not-allowed;
}
</style>

目前这个已经封装好一个组件了,可以拿走在项目中直接调用了。

总结

对于实现一些操作页面的功能来说,canvas是个强大的处理方法,还是要加强对canvas的学习和使用。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

聊一聊cropper.js 的相关文章

  • 文本编辑工具vim-及特殊用法,alias别名

    文章目录 简介打开文件 一 vim三种模式模式转换关闭文件特殊用法 xff1a 二 命令模式1 命令模式查找2 命令模式光标跳转3 命令模式翻屏操作4 字符编辑 xff1a 5 替换命令 r replace 6 删除命令 xff1a 7 复
  • 企业竞争分析的几种方法:SWOT、波特五力、PEST

    最近实验室要申报一个互联网 43 的项目 xff0c 项目中有关企业经营部分的内容着实令我们这些工科生无从下手 xff0c 在咨询了某专业相关的学妹后稍微有了点头绪 此处手动感谢学妹的协助哈哈哈 xff0c 本着学科交叉 xff0c 多学无
  • 解决E: 仓库 “http://ppa.launchpad.net/fcitx-team/nightly/ubuntu bionic Release” 没有 Release 文件。

    今天 xff0c 在更新软件时 xff0c 使用以下命令时 sudo apt get update sudo apt get upgrade 抛出错误 E 仓库 http ppa launchpad net fcitx team night
  • (仿牛客社区项目)Java开发笔记3.5:添加评论

    文章目录 添加评论1 dao层2 service层3 controller层4 view层5 功能测试 添加评论 根据上节的开发安排 xff1a 显示评论功能完成后 xff0c 开始实现添加评论功能 1 dao层 CommentMapper
  • js_事件

    一 常用的事件 onload 加载完成事件 页面加载完成之后 常用于做页面js代码初始化操作 onclick 单击事件 常用于按钮的点击相应操作 onblur 失去焦点事件 常用于输入框失去焦点后验证其输入内容是否合法 onchange 内
  • 操作系统学习

    目录 2 1 操作系统的启动 3 1 内存分层结构 3 2 地址空间与地址生成 3 3 内存分配 3 4 压缩式与交换式碎片整理 4 1 非连续内存 分段 4 2 非连续内存 分页 4 3 页表概述 4 4 多级页表 4 5 反向页表 5
  • 更改 tr 背景颜色无效问题

    更改tr背景颜色无效问题 x1f4c3 在更改tr背景颜色时 xff0c 我们肯定是想要整行颜色改变 xff0c 但有时会出现只有部分改变 或 全都不改变的情况 这时我们就需要去看一下自己是否在之前设计的 CSS 样式中已经给定了tr中的t
  • 【以例为引】gtsam简单入门(上)--理论和认识

    如有错漏 xff0c 请评论或者私信指出 xff0c 感谢 xff01 xff01 GTSAM简介 GTSAM xff08 Georgia Tech Smoothing and Mapping xff09 是基于因子图的C 43 43 库
  • 基于51单片机的门禁卡设计

    1 设计思路 RFID门禁系统主要采用了STC89C52RC单片机作为控制模块及读卡器RFID RC522作为识别模块 本设计实现了自动 准确的识别卡序列号 当有卡进入到读卡器读卡的范围内时就会读取到相应的卡序列号 xff0c 并根据得到的
  • STM8S程序烧录失败?调试?ST-Link方式新手向教程IAR

    首先我们要接线 xff0c 以上为某块STM8S的原理图 xff0c 我们要SWIM接SWIM xff0c NSET接RESET xff0c GND接GND xff0c 3 3接3 3 接线完成后就是软件部分了 软件部分首先要下载ST li

随机推荐

  • 机器学习算法——K-近邻算法(代码实现手写数字识别)

    0 引言 xff0c K 近邻算法是一种非常有效的分类算法 xff0c 它非常有效且易于掌握 原理 xff1a K 近邻算法通过计算不同样本之间的距离来分类物品 使用前 xff0c 我们需要有一个训练样本集 xff0c 并且样本集中每个数据
  • 为Navigation 2创建自定义behavior tree plugin

    系列文章目录 思岚激光雷达rplidar从ROS 1到ROS 2的移植 ROS 2下navigation 2 stack的构建 订阅rviz2的导航目标位置消息 goal pose 打断behavior tree的异步动作节点 xff0c
  • ubuntu20:/usr/bin/env: ‘python’: No such file or directory

    参考 xff1a https stackoverflow com questions 3655306 ubuntu usr bin env python no such file or directory 第一种可能 xff1a 如果没装p
  • 四轴无人飞行器 之 上位机

  • c/c++编程学习:空指针是什么?

    什么是空指针 xff1f 对于每一种指针类型 xff0c 都有一个特殊的值 空指针 xff0c 空指针与其他所有指针值区分开来 xff0c 保证其不会指向任何函数或者对象等有意义的数据 因此 xff0c 取地址运算符 amp 永远不会产生空
  • 基于ESP32的智能车WiFi图传模块实现

    基于 ESP32 C3 的多协议 WiFi 透传模块 xff08 可用作智能车图传 xff09 本项目为基于乐鑫公司的 ESP32 C3 芯片制作的无线透传模块 xff0c 具有多个通信协议接口 xff1a UART SPI 设计初衷是为了
  • 云服务器下载的镜像文件raw格式转vmdk

    使用软件qemu img https qemu weilnetz de w64 2021 下载之后安装 xff0c 然后进入安装的文件夹 xff0c 打开命令行工具然后执行下面命令 qemu img exe convert p f raw
  • keil5使用Arm Compiler 6编译出错

    Using Compiler 39 V6 15 39 folder 39 D Keil v5 ARM ARMCLANG Bin 39 main c 16 warning In file included from USER stm32f4x
  • 浏览器的相关知识

    今天在网上找到了一些需要大致了解的有关浏览器的相关知识分享 xff0c 原文链接在下方 1 浏览器的主要组成部分是什么 xff1f 用户界面 包括地址栏 前进 后退按钮 书签菜单等 除了浏览器主窗口显示的您请求的页面外 xff0c 其他显示
  • MySQL--用Navicat连接MySQL8.0报错1251问题解决

    文章目录 一 安装后直接用Navicat连接1251报错二 仍报错为 39 mysql 39 不是内部或外部命令 1 环境变量配置 三 找不到MySQL Server 8 0 bin路径四 解决上述全部问题 一 安装后直接用Navicat连
  • 10 分钟让你明白 MySQL 是如何利用索引的

    一 前言 在MySQL中进行SQL优化的时候 xff0c 经常会在一些情况下 xff0c 对 MySQL 能否利用索引有一些迷惑 譬如 MySQL 在遇到范围查询条件的时候就停止匹配了 xff0c 那么到底是哪些范围条件 xff1f MyS
  • 吊炸天的 Docker 图形化工具 —— Portainer

    一 Docker图形化工具二 DockerUI三 船坞四 搬运工1 查看portainer平均值2 选择喜欢的portainer风格整合 xff0c 下载3 启动dockerui容器4 xff0c 网页管理 一 Docker图形化工具 Do
  • 为提高面试通过率,技术岗可以提前做好哪些面试准备?

    Hi xff0c 大家好 xff0c 我是小庄 目前2023届秋招提前批已经陆续开始了 xff0c 考虑到一些校招的同学可能是第一次接触面试 xff08 该文章适用于校招 社招 xff09 xff0c 所以这篇文章就是为了记录一些面试技巧
  • GNU Radio自定义模块:Embedded Python Block的使用

    GNU Radio 学习使用 OOT 系列教程 xff1a GNU Radio3 8创建OOT的详细过程 基础 C 43 43 GNU Radio3 8创建OOT的详细过程 进阶 C 43 43 GNU Radio3 8创建OOT的详细过程
  • 中文分词

    本文首先介绍下中文分词的基本原理 xff0c 然后介绍下国内比较流行的中文分词工具 xff0c 如jieba SnowNLP THULAC NLPIR xff0c 上述分词工具都已经在github上开源 xff0c 后续也会附上github
  • (1)GNSS驱动nmea_navsat_driver 功能包的使用

    总览 该软件包为输出兼容NMEA语句的GPS设备提供了ROS接口 有关原始格式的详细信息 xff0c 请参见NMEA句子的GPSD文档 在成千上万的NMEA兼容GPS设备中 xff0c 我们正在汇编已知支持的设备列表 这个包是与兼容geog
  • (2)ROS传感器之GPS实践

    一 GPS接口类型 GPS接口大体可以分为两类 xff0c 一是单独的GPS接收器 xff0c 通常为USB接口 xff1b 二是与其他传感器集成 xff0c 例如激光雷达或者imu xff0c 大多是USB或者网络接口 xff0c 本文主
  • (6)GPS坐标与UTM坐标的转换

    1 简介 1 1 消息 gps common定义了两个通用消息 xff0c 供GPS驱动程序输出 xff1a gps common GPSFix和gps common GPSStatus 在大多数情况下 xff0c 这些消息应同时发布 xf
  • scanf("%c",&m)中%c前面加空格的作用

    c前面加空格不是必须的 xff0c 但有了空格就可以忽略你输入的空格 例如 xff1a scanf 34 c 34 amp m xff0c 你输入了 a a前面有个空格 xff0c a就能被c接受 但控制符前如果没空格 xff0c 那c就接
  • 聊一聊cropper.js

    最近的项目中有一个纯前端实现的功能困扰了我好久 xff0c 就是用户上传图片以后需要用户进入图片裁剪页并完成上传的功能 xff0c 一开始我是打算自己去用canvas去写这样一个页面的 xff0c 但是项目开发周期短 xff0c 任务紧 x