基于canvas画布的实用类Fabric.js的使用

news2024/11/18 3:30:03

目录

前言

一、Fabric.js简介

二、开始

1、引入Fabric.js

2、在main.js中使用

3、初始化画布

三、方法

四、事件

1、常用事件

2、事件绑定

3、事件解绑

五、canvas常用属性

六、对象属性

1、基本属性

2、扩展属性

七、图层层级操作

八、复制和粘贴

1、复制

2、粘贴

九、锁定

1、静止水平移动(lockMovementX)

2、静止垂直移动(lockMovementY)

3、静止旋转(lockRotation)

4、静止水平缩放(lockScalingX)

5、静止垂直缩放(lockScalingY)

6、限制拖动区域

十、分组

十一、动画

十二、图像滤镜

十三、渐变

1、线性渐变

2、径向渐变

十四、拖拽和缩放画布

1、拖拽画布

2、以画布中心点为基准手动缩放

3、以鼠标指针位置为基准缩放

十五、右键菜单删除

十六、自由绘画

1、开启绘图模式

2、关闭绘图模式

十七、绘制背景图片

1、方式一:通过img元素添加

2、方式二:通过图片路径添加

十八、绘制文本

1、基础用法

2、文本修饰

3、可编辑文本

十九、绘制线和路径

1、绘制直线

2、绘制虚线

3、绘制路径

二十、自由绘制矩形

二十一、自由绘制圆形

二十二、自由绘制椭圆形

二十三、自由绘制三角形

二十四、自由绘制多边形


前言

        演示Demo:前端可视化demo

        Demo源码:https://gitee.com/k21vin/front-end-data-visualization

        本文章所有的gif图片由于录屏软件问题,上面的鼠标位置都是错位显示的!!

一、Fabric.js简介

        Fabric.js是一个对canvas进行封装的Javascript库,在原生canvas之上提供了交互式对象模型,通过简洁的api就可以在画布上进行丰富的操作。

        它主要的功能包括在canvas上创建和填充图形,比如矩形、圆形、多边形;生成的图像自带缩放、旋转、拖拽等功能;还可以给图形填充渐变颜色;各个图形可以相互组合等等。

二、开始

1、引入Fabric.js

npm i fabric --save

2、在main.js中使用

// main.js
import fabric from "fabric"
Vue.use(fabric)

3、初始化画布

<template>
  <canvas id="canvas"></canvas>
</template>

<script>
export default {
  data () {
    canvas: null, // 画布对象
  },
  mounted() {
    this.canvas = new fabric.Canvas('canvas', {
      width: 300,
      height: 200
    })
  }
</script>

三、方法

canvas.add(object)                   // 添加对象
canvas.remove(object)                // 删除对象
canvas.setWidth(width)               // 设置canvas宽度
canvas.setHeight(height)             // 设置canvas高度
canvas.setDimensions({width, height})// 一键设置宽高
canvas.getObjects()                  // 获取所有对象
canvas.getActiveObject()             // 获取选中的对象
canvas.clear()                       // 清除画布中所有对象
canvas.renderAll()                   // 重绘
canvas.requestRenderAll()            // 请求重新渲染
canvas.getZoom()                     // 获取画布当前缩放值
canvas.sendToBack(object)            // 移到对象到最底层
canvas.viewportCenterObjectH(object) // 水平居中对象
canvas.viewportCenterObjectV(object) // 垂直居中对象
canvas.viewportCenterObject(object)  // 垂直水平居中对象
canvas.fxCenterObjectH(object)       // 动画水平居中对象
canvas.fxCenterObjectV(object)       // 动画垂直居中对象
canvas.fxCenterObject(object)        // 动画垂直水平居中对象

let canvasJsonData = JSON.stringify(canvas.toJSON()) // 将画布序列化成json数据
let canvasSvgData = canvas.toSVG() // 将画布序列化成svg数据
canvas.loadFromJSON(canvasJsonData)  // 反序列化Json数据

四、事件

1、常用事件

mouse: down         // 鼠标按下事件
mouse: move         // 鼠标移动事件
mouse: up           // 鼠标移动事件
mouse: over         // 鼠标移入事件
mouse: out          // 鼠标移出事件
mouse: dblclick     // 鼠标双击事件
object: added       // 对象被添加事件
object: removed     // 对象被删除事件
object: modified    // 对象被修改事件
object: rotating    // 对象被旋转事件
object: scaling     // 对象被缩放事件
object: moving      // 对象被移动事件

2、事件绑定

canvas.on('mouse: wheel', (opt) => {
  console.log(opt)
})

3、事件解绑

canvas.off('mouse: wheel')

五、canvas常用属性

canvas.selection = true                  // 画布是否可选中 默认为true、false:不可选中
canvas.selectionColor = 'transparent'    // 画布鼠标框选时的背景色
canvas.selectionBorderColor = 'transparent'// 画布鼠标框选时的边框颜色
canvas.selectionLineWidth = 6            // 画布鼠标框选时的边框厚度
canvas.selectionDashArray = [30, 4, 10] // 画布鼠标框选时边框虚线规则
canvas.selectionFullyContained = true   // 只选择完全包含在拖动选择矩形中的形状
canvas.backgroundColor = '#2E3136'      // 画布背景色
canvas.hoverCursor = 'pointer'          // 鼠标光标样式 default、pointer、move等
canvas.skipTargetFind = true            // 整个画板元素不能被选中
canvas.fireRightClick = true            // 启用右键,options.button的数字为3
canvas.stopContextMenu = true           // 禁止默认右键菜单

        Fabric.js 可以通过 viewportTransform 属性配置画布的视窗属性

canvas.viewportTransform[4] = 100
canvas.viewportTransform[5] = 100

        viewportTransform 是一个数组,里面有6个元素,默认值是 [1, 0, 0, 1, 0, 0]。从下标0开始,它们分别代表:

[0]: 水平缩放(x轴方向)
[1]: 水平倾斜(x轴方向)
[2]: 垂直倾斜(y轴方向)
[3]: 垂直缩放(y轴方向)
[4]: 水平移动(x轴方向)
[5]: 垂直移动(y轴方向)

六、对象属性

1、基本属性

let circle = new fabric.Circle({
  top: 100,    // y坐标
  left: 100,    // x坐标
  fill: '#17b978', // 填充色
  radius: 50  // 半径
})
rect.top = 100                       // y坐标
rect.left = 100                      // x坐标
rect.width = 100                     // 矩形宽度
rect.height = 100                    // 矩形高度
circle.radius = 50                   // 圆半径
rect.fill = '#17b978'                // 填充色
rect.stroke = '#FE5332'              // 线条颜色
rect.strokeWidth = 10                // 线条宽度
rect.strokeMiterLimit = index        // 可以用来记录当前选中的rectList列表的索引!!!!!

2、扩展属性

circle.set({
  hasBorders: false
}
circle.selectable = false                 // 控件不能被选择,不会被操作
circle.hasControls = false                // 只能移动不能(编辑)操作
circle.hasBorders = false                 // 选中时,是否显示边,true:显示(默认)
circle.borderColor = 'red'                // 选中时,边的颜色
circle.borderScaleFactor = 5              // 选中时,边的粗细
circle.borderDashArray = [20, 5, 10, 7]  // 选中时,虚线边的规则
circle.transparentCorners = false         // 选中时,角是否是空心 true:空心  false:实心
circle.cornerColor = "#a1de93",           // 选中时,角的颜色
circle.cornerStrokeColor = 'pink'         // 选中时,角的边框的颜色
circle.cornerStyle = 'circle'             // 选中时,角的属性  rect:矩形(默认)、circle:圆形
circle.cornerSize = 20                    // 选中时,角的大小为20
circle.cornerDashArray = [10, 2, 6]       // 选中时,虚线角的规则
circle.selectionBackgroundColor = '#ffc300' // 选中时,选框的背景色
circle.padding = 20                       // 选中时,选框离图形的距离
circle.borderOpacityWhenMoving = 0.6      // 当对象活动和移动时,对象控制边界的不透明度
triangle.perPixelTargetFind = true        // 选择三角形空白位置的时候无法选中,false:可以选中(默认)

七、图层层级操作

canvas.bringToFront(rect)    // 移到顶层
canvas.sendToBack(rect)      // 移到底层
canvas.bringForward(rect)    // 上移一层
canvas.sendBackwards(rect)   // 下移一层
canvas.moveTo(0)          // 移动到指定层

八、复制和粘贴

1、复制

handleCopy() {
  if (!canvas.getActiveObject()) {
    this.$message.warning('请先选择元素')
    return
  }
  this.canvas.getActiveObject().clone(cloned => {
    this.cloneObjects = cloned
  })
}

2、粘贴

handlePaste() {
  if (!this.cloneObjects) {
    return this.$message.warning('还没复制过任何内容')
  }
  this.cloneObjects.clone(cloned => {
    this.canvas.discardActiveObject() // 取消选择
    // 设置新内容的坐标位置
    cloned.set({
      left: cloned.left + 10,
      top: cloned.top + 10,
      evented: true
    })
    if (cloned.type === 'activeSelection') { // 如果复制的是多个对象,则需要遍历克隆对象
      cloned.canvas = this.canvas;
      cloned.forEachObject(obj => {
        this.canvas.add(obj)
      })
      cloned.setCoords()
    } else {
      this.canvas.add(cloned)
    }
    this.cloneObjects.top += 10
    this.cloneObjects.left += 10
    this.canvas.setActiveObject(cloned)
    this.canvas.requestRenderAll()
  })
}

九、锁定

        Fabric对象可以添加一些属性进行锁定,例如静止水平移动、静止垂直移动,静止缩放等等

1、静止水平移动(lockMovementX)


let rect = new fabric.Rect({
  width: 100,
  height: 50,
  fill: '#ffde7d',
  top: 20,
  left: 20
})
rect.lockMovementX = true
canvas.add(rect)

2、静止垂直移动(lockMovementY)


let rect = new fabric.Rect({
  width: 100,
  height: 50,
  fill: '#ffde7d',
  top: 20,
  left: 20
})
rect.lockMovementY = true

3、静止旋转(lockRotation)


let rect = new fabric.Rect({
  width: 100,
  height: 50,
  fill: '#ff9a3c',
  top: 60,
  left: 160
})
rect.lockRotation = true

4、静止水平缩放(lockScalingX)


let rect = new fabric.Rect({
  width: 100,
  height: 50,
  fill: '#ffde7d',
  top: 20,
  left: 20
})
rect.lockScalingX = true

5、静止垂直缩放(lockScalingY)


let rect = new fabric.Rect({
  width: 100,
  height: 50,
  fill: '#f95959',
  top: 20,
  left: 20
})
rect.lockScalingY = true

6、限制拖动区域


let boundingBox = new fabric.Rect({
  top: 100,
  left: 100,
  width: 600,
  height: 400,
  fill: '#f95959',
  selectable: false
})
let movingBox = new fabric.Rect({
  top: 150,
  left: 150,
  width: 100,
  height: 100,
  fill: 'yellow',
  hasBorders: false,
  hasControls: false,
  hoverCursor: 'move'
})
this.canvas.add(boundingBox);
this.canvas.add(movingBox);
this.canvas.on("object:moving", (opt) => {
  let top = movingBox.top;
  let left = movingBox.left;
  let topBound = boundingBox.top;
  let bottomBound = topBound + boundingBox.height;
  let leftBound = boundingBox.left;
  let rightBound = leftBound + boundingBox.width;
  opt.target.left = Math.min(Math.max(left, leftBound), rightBound - movingBox.width)
  opt.target.top = Math.min(Math.max(top, topBound), bottomBound - movingBox.height)
})

十、分组

        Groups是Fabric最强大的功能之一,它可以将任意数量的Fabric对象组合在一起,形成一个小组,分组后,所有对象都可以一起移动、修改、缩放、旋转甚至更改其外观等


let group = new fabric.Group([circle, text], {
  left: 100,
  top: 100,
  angle: -10
})
canvas.add(group)

        修改分组的某个对象的属性:


group.item(0).set("fill","red");
group.item(1).set({
  text:"trololo",
  fill:"white"
})

        分组时要记住的另一件事是对象的状态。例如,在与图像组成组时,需要确保这些图像已完全加载:


fabric.Image.fromURL(logo, img => {
  let img1 = img.scale(0.3).set({left: 0, top: 0})

  fabric.Image.fromURL(logo, img => {
    let img2 = img.scale(0.3).set({left: 80, top: 0})

    fabric.Image.fromURL(logo, img => {
      let img3 = img.scale(0.3).set({left: 160, top: 0})
      
      let group = new fabric.Group([img1, img2, img3], {
        left: 10,
        top: 400
      })
      canvas.add(group)
    })
  })
})

十一、动画

        每个Fabric对象都有一个animate方法,该方法可以动画化该对象,animate(动画属性,动画的结束值,[动画的详细信息])


let rect = new fabric.Rect({
  left: 100,
  top: 100,
  width: 100,
  height: 100,
  fill: 'red'
})
rect.animate("angle", 45, {
  onChange: canvas.renderAll.bind(canvas)
})
canvas.add(rect)

        第一个参数是要设置动画的属性。第二个参数是动画的结束值。如果矩形具有-15°的角度,并且我们传递了45,则动画将从-15°变为45°。第三个参数是一个可选对象,用于指定动画的详细信息-持续时间,回调,缓动等


rect.animate("angle", 45, {
  from: 0, // 允许指定可设置动画的属性的起始值(如果我们不希望使用当前值)
  duration: 1000, // 默认为500(ms),可用于更改动画的持续时间
  easing: fabric.util.ease.easeOutBounce, // 缓动功能 easeOutBounce、easeInCubic、easeOutCubic、easeInElastic、easeOutElastic、easeInBounce、easeOutExpo
  onChange: canvas.renderAll.bind(canvas), // 在每次刷新时都会执行
  onComplete: (e) => { console.log(e) } // 在动画结束时调用的回调
})

        animate的一个方便之处在于它还支持相对值


// 向右移动100px
rect.animate('left', '+=100', {
  onChange: canvas.renderAll.bind(canvas)
})

// 逆时针旋转5度
rect.animate('angle', '-=5', {
  onChange: canvas.renderAll.bind(canvas)
})

十二、图像滤镜

        fabric.Image的每个实例都具有“ filters”属性,该属性是一个简单的过滤器数组。该阵列中的每个过滤器都是Fabric过滤器之一的实例。或您自己的自定义过滤器的实例。


let url = 'http://localhost:82/public/img/5.png' // 图片url
let base64Url = await this.imgUrlToBase64(url) // 图片base64 url
// 正常照片
fabric.Image.fromURL(url, img => {
  img.scale(0.3)
  canvas.add(img)
})

// 单个滤镜
fabric.Image.fromURL(base64Url, img => {
  img.scale(0.3)
  img.left = 300
  // 添加滤镜
  img.filters.push(new fabric.Image.filters.Grayscale())
  // 图片加载完成之后,应用滤镜效果
  img.applyFilters()
  canvas.add(img)
})

// 叠加滤镜
fabric.Image.fromURL(base64Url, img => {
  img.scale(0.3)
  img.set({
    left: 300,
    top: 250,
  })
  img.filters.push(
    new fabric.Image.filters.Grayscale(),
    new fabric.Image.filters.Sepia(), //色偏
    new fabric.Image.filters.Brightness({ brightness: 0.2 }) //亮度
  )
  img.applyFilters()
  canvas.add(img)
})

        (说明:这里图片可能会出错,放本地图片地址会报“Cannot read property 'naturalWidth' of null”的错误,直接放网络图片地址会报“Failed to execute 'texImage2D' on 'WebGLRenderingContext': The image element contains cross-origin data, and may not be loaded.”的错误。解决方法就是将转为Base64格式

十三、渐变

        Fabric支持在所有对象上设置填充或描边属性的渐变,首先创建渐变,然后将其分配给填充或描边。

1、线性渐变


// 圆
let circle = new fabric.Circle({
  left: 100,
  top: 100,
  radius: 50,
})
let gradient = new fabric.Gradient({
  type: 'linear', // linear or radial
  gradientUnits: 'pixels', // pixels or pencentage 像素 或者 百分比
  coords: { x1: 0, y1: 0, x2: circle1.width, y2: 0 }, // 至少2个坐标对(x1,y1和x2,y2)将定义渐变在对象上的扩展方式
  colorStops:[ // 定义渐变颜色的数组
    { offset: 0, color: 'red' },
    { offset: 0.2, color: 'orange' },
    { offset: 0.4, color: 'yellow' },
    { offset: 0.6, color: 'green' },
    { offset: 0.8, color: 'blue' },
    { offset: 1, color: 'purple' },
  ]
})
circle.set('fill', gradient);
canvas.add(circle)

2、径向渐变


let circle = new fabric.Circle({
  left: 100,
  top: 100,
  radius: 50
})
let gradient = new fabric.Gradient({
  type: 'radial',
  coords: {
    r1: 50,
    r2: 0,
    x1: 50,
    y1: 50,
    x2: 50,
    y2: 50,
  },
  colorStops: [
    { offset: 0, color: '#fee140' },
    { offset: 1, color: '#fa709a' }
  ]
})
circle.set('fill', gradient);
canvas.add(circle)

十四、拖拽和缩放画布

1、拖拽画布


<script>
  export default {
    data() {
      return {
        lastPosX: 0,       // 上次鼠标位置X坐标
        lastPosY: 0,       // 上次鼠标位置Y坐标
        isDragging: false, // 是否可以拖拽画布
      }
    },
    mounted() {
      ... // 初始化canvas
      this.canvas.on('mouse:down', this.onMouseDown)
      this.canvas.on('mouse:move', this.onMouseMove)
      this.canvas.on('mouse:up', this.onMouseUp)
    },
    methods: {
      // 监听鼠标按下事件
      onMouseDown(opt) {
        this.lastPosX = opt.e.clientX
        this.lastPosY = opt.e.clientY
        this.isDragging = true
      },
      // 监听鼠标移动事件
      onMouseMove(opt) {
        if (this.isDragging) {
          this.canvas.viewportTransform[4] += opt.e.clientX - this.lastPosX
          this.canvas.viewportTransform[5] += opt.e.clientY - this.lastPosY
          this.canvas.requestRenderAll()
          this.lastPosX = opt.e.clientX
          this.lastPosY = opt.e.clientY
        }
      },
      // 监听鼠标松开事件
      onMouseUp(opt) {
        if (this.isDragging) {
          this.canvas.setViewportTransform(this.canvas.viewportTransform)
          this.isDragging = false
        }
      }
    }
  }
</script>

2、以画布中心点为基准手动缩放

<template>
  <el-tooltip content="放大" placement="bottom-start">
    <span class="iconfont icon-fangda" @click="onManualScale(-100)"></span>
  </el-tooltip>
 <el-tooltip content="缩小" placement="bottom-start">
    <span class="iconfont icon-suoxiao" @click="onManualScale(100)"></span>
  </el-tooltip>
</template>
<script>
export default {
  // 中心点缩放画布
  onManualScale(delta) {
    let zoom = canvas.getZoom() // 获取画布当前缩放值
    zoom *= 0.999 ** delta
    zoom = zoom > 10 ? 10 : (zoom < 0.1 ? 0.1 : zoom) // 最大放大10倍,最小缩小至10%
    canvas.zoomToPoint({ // 以画布中心点为基准缩放
      x: this.canvasBoxWidth / 2,  // canvasBoxWidth 画布宽度
      y: this.canvasBoxHeight / 2  // canvasBoxHeight 画布高度
    }, zoom)
  }
}
</script>

3、以鼠标指针位置为基准缩放


this.canvas.on('mouse:wheel', this.onMouseWheel)

// 监听鼠标放大缩小事件
onMouseWheel(opt) {
  let delta = opt.e.deltaY // 滚轮,向上滚一下是 -100,向下滚一下是 100
  let zoom = this.canvas.getZoom() // 获取画布当前缩放值
  zoom *= 0.999 ** delta
  zoom = zoom > 10 ? 10 : (zoom < 0.1 ? 0.1 : zoom) // 最大放大10倍,最小缩小至10%
  this.canvas.zoomToPoint({ // 以鼠标指针位置为基准缩放
    x: opt.e.offsetX,
    y: opt.e.offsetY
  }, zoom)
  opt.e.preventDefault()
  opt.e.stopPropagation()
}

十五、右键菜单删除

<template>
  <div class="canvas-box" ref="canvasBox">
    <canvas id="canvas"></canvas>
    <div id="delMenu" ref="delMenu" v-show="isShowDelMenu" :style="delMenuStyle" @contextmenu.prevent="">
      <el-button type="iconButton" icon="h-icon-delete" @click="handleDeleteMenu">删除</el-button>
    </div>
  </div>
</template>
<script>
export default {
  name: 'PointerDetail',
  data () {
    return {
      canvas: null,
      activeEle: null, // 上次选中元素
      isShowDelMenu: false, // 是否显示删除弹窗
      delMenuStyle: '' // 删除弹窗定位样式
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    // 初始化
    init() {
      this.canvas = new fabric.Canvas('canvas', {
        fireRightClick: true, // 启用右键,button的数字为3
        stopContextMenu: true, // 禁止默认右键菜单
      })
      this.canvas.on('mouse:down', this.onMouseDown)
    },
    // 监听鼠标按下事件
    onMouseDown(opt) {
      // 还原上次选中状态
      if (this.activeEle) {
        this.activeEle.set('fill', 'transparent')
        this.canvas.renderAll()
      }
      this.activeEle = opt.target || null
      // 按下鼠标右键
      if (opt.button === 3) {
        // 点击到非图片控件 显示删除弹窗和填充控件背景色
        if (opt.target && opt.target.type !== 'image') {
          this.activeEle.set('fill', 'rgba(100, 100, 255, 0.3)')
          this.canvas.renderAll()
          this.isShowDelMenu = true
          this.$nextTick(() => {
            this.delMenuStyle = this.getMenuStyle(this.$refs.delMenu, opt)
          })
        } else {
          // 否则隐藏删除弹窗
          this.hiddenMenu()
        }
      // 按下鼠标左键
      } else {
        this.hiddenMenu()
      }
    },
    // 获取弹窗坐标
    getMenuStyle(ele, opt) {
      let menuWidth = ele.offsetWidth
      let menuHeight = ele.offsetHeight
      let pointX = opt.pointer.x + 2
      let pointY = opt.pointer.y + 2
      if (this.$refs.canvasBox.offsetWidth - pointX <= menuWidth) {
        pointX -= menuWidth
      }
      if (this.$refs.canvasBox.offsetHeight - pointY <= menuHeight) {
        pointY -= menuHeight
      }
      return `left: ${pointX}px; top: ${pointY}px;`
    },
    // 隐藏菜单
    hiddenMenu() {
      this.activeEle = null
      this.isShowDelMenu = false
    },
    // 删除选中元素
    handleDeleteMenu() {
      this.canvas.remove(this.activeEle)
      this.canvas.requestRenderAll()
      this.hiddenMenu()
    }
  }
}

十六、自由绘画

1、开启绘图模式


let canvas = new fabric.Canvas('canvas', {
  isDrawingMode: true // 开启绘图模式
})
canvas.freeDrawingBrush.color = '#11999e' // 设置画笔颜色
canvas.freeDrawingBrush.width = 10 // 设置画笔粗细
canvas.freeDrawingBrush.shadow = new fabric.Shadow({ // 设置画笔投影
  blur: 10,
  offsetX: 10,
  offsetY: 10,
  affectStroke: true,
  color: '#30e3ca'
})

2、关闭绘图模式


canvas.isDrawingMode = false

十七、绘制背景图片

1、方式一:通过img元素添加


<img src="@/assets/images/logo.png" id="logo">

let img = document.getElementById('logo')
img.onload = () => {
  let canvasImage = new fabric.Image(imgElement, {
    left: 100, // 距离画布左侧距离
    top: 100, // 距离画布顶部距离
    width: 200, // 图片宽度
    height: 200, // 图片高度
    angle: 50, // 旋转
    opacity: 1 // 透明度
  })
  canvas.add(canvasImage)
}

2、方式二:通过图片路径添加


let url = 'http://localhost:82/public/img/logo.png'
fabric.Image.fromURL(url, img => {
  let canvasImage = img.set({
    scaleX: 0.5,
    scaleY: 0.5
  })
  canvas.add(canvasImage)
}) 

十八、绘制文本

        Fabric也提供了文本的相关功能,Fabric文本允许以面向对象的方式处理文本,原生canvas方法,只允许在非常低的级别上填充或描边文本,通过实例化fabric.Text实例,我们就可以使用文本,就像我们将使用任何其他Fabric对象:移动它,缩放它,更改其属性等, 其次它提供比canvas给我们更丰富的功能,包括:


Multiline support   // 支持多行
Text alignment      // 文本对齐 Left、center、right
Text background     // 文本背景 背景也遵循文本对齐
Text decoration     // 文字装饰 下划线Underline、上划线overline、贯穿线strike-through
Line height         // 行高 使用多行文字时出错
Char spacing        // 字符间距 使文本更紧凑或间距更大
Subranges           // 子范围 将颜色和属性应用于文本对象的子范围
Multibyte           // 多字节 支持表情符号
On canvas editing   // 交互式画布编辑 可以直接在canvas上键入文本

1、基础用法


let text = new fabric.Text('Hello World!', {
  left: 40,
  top: 10,
  fontFamily: 'Comic Sans', // 字体
  fontSize: 60, // 字号
  fontWeight: 600, // 字体重量(粗细),normal、bold 或 数字(100、200、400、600、800)
  fontStyle: 'normal', // 字体风格 正常 normal 或 斜体 italic
  charSpacing: 100, // 字距
  fill: 'red', // 字体颜色
  cornerColor: 'pink', // 角的颜色(被选中时)
  angle: 30, // 旋转
  backgroundColor: '#ffd460', // 背景色
  borderColor: 'yellowGreen', // 边框颜色(被选中时)
  borderScaleFactor: 4, // 边框粗细(被选中时)
  borderDashArray: [10, 4, 20], // 创建边框虚线
  stroke: '#3f72af', // 文字描边颜色(蓝色)
  strokeWidth: 2, // 文字描边粗细
  textAlign: 'left', // 对齐方式:left 左对齐; right 右对齐; center 居中
  opacity: 0.8, // 不透明度
  // text: '雷猴', // 文字内容,会覆盖之前设置的值
  selectable: true, // 能否被选中,默认true
  shadow: 'rgba(0, 0, 0, 0.5) 5px 5px 5px', // 投影
})
canvas.add(text)

2、文本修饰


// 下划线
let underlineText = new fabric.Text("I am an undrline text", {
  underline: true
})
canvas.add(underlineText)

// 贯穿线
let strokeThroughText = new fabric.Text("I am a stroke-through text", {
  linethrough: true,
  top: 40
})
canvas.add(strokeThroughText)

// 上划线
let overlineText = new fabric.Text("I am overline text", {
  overline:true,
  top: 80
})
canvas.add(overlineText)

3、可编辑文本


let IText = new fabric.IText('雷猴啊,双击打几个字试下~', {
  fontFamily: 'Comic Sans'
})
canvas.add(IText)

十九、绘制线和路径

1、绘制直线


let line = new fabric.Line([0, 100, 100, 100], {
  fill: 'green', // 填充色
  stroke: 'green', // 笔触颜色
  strokeWidth: 2, // 笔触宽度
});
canvas.add(line);

2、绘制虚线

        在绘制直线的基础上添加属性strokeDashArray[a,b],表示每隔a个像素空b个像素。


let line = new fabric.Line([0, 100, 100, 100], {
  fill: 'green', // 填充色
  stroke: 'green', // 笔触颜色
  strokeWidth: 2, // 笔触宽度
  strokeDashArray:[3,1]
});
canvas.add(line);

3、绘制路径

  • Fabric.js使用 new fabric.Path 创建路径。
  • M:可以理解为新的起始点 x,y 坐标
  • L:每个折点的 x,y 坐标
  • z:自动闭合(自动把结束点和起始点连接起来)


let path = new fabric.Path('M 0 0 L 200 100 L 170 200 z')
path.set({
    top: 120, // 距离容器顶部距离 120px
    left: 120, // 距离容器左侧距离 120px
    fill: 'hotpink', // 填充 亮粉色
    opacity: 0.5, // 不透明度 50%
    stroke: 'black', // 描边颜色 黑色
    strokeWidth: 10 // 描边粗细 10px
})

        上述代码第一行“M”代表“移动”命令,“M 0 0” 代表把画笔移动到(0, 0)点坐标。“L”代表“线”,“L 200 100 ”的意思是使用钢笔画一条线,从(0, 0)坐标画到(200, 100)坐标。“z” 代表让图形闭合路径。这样就画出了一个三角形。画好三角形后,我们可以用set( )方法对三角形的位置、颜色、角度、透明度等属性进行设置。

二十、自由绘制矩形

<template>
  <div class="canvas-box" ref="canvasBox">
    <canvas id="canvas"></canvas>
    <el-button @click="handleActiveRect">绘制矩形</el-button>
  </div>
</template>
<script>
export default {
  name: 'PointerDetail',
  data () {
    return {
      canvas: null,
      lastPoint: null, // 上次鼠标点位坐标
      strokeColor: 'transparent', // 轮廓填充颜色
      isActiveRect: false, // 是否激活绘制矩形
      rectList: [] // 绘制的矩形列表
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    // 初始化
    init() {
      this.canvas = new fabric.Canvas('canvas', {
        width: this.$refs.canvasBox.offsetWidth,
        height: this.$refs.canvasBox.offsetHeight,
        backgroundColor: '#2E3136',
        selectionColor: 'transparent',
        selectionBorderColor: 'transparent',
        hoverCursor: 'default'
      })
      this.canvas.on('mouse:down', this.onMouseDown)
      this.canvas.on('mouse:up', this.onMouseUp)
      this.canvas.on('object:added', this.onObjectAdded)
    },
    // 监听鼠标按下事件
    onMouseDown(opt) {
      if (this.isActiveRect) {
        this.lastPoint = opt.absolutePointer || null
        this.strokeColor = '#00FF64'
      }
    },
    // 监听鼠标松开事件
    onMouseUp(opt) {
      if (this.isActiveRect) {
        this.drawRect(opt.absolutePointer)
      }
    },
    // 绘制完成元素事件
    onObjectAdded(opt) {
      let target = opt.target
      if (target.stroke === '#00FF64') {
        this.isActiveRect && this.rectList.push(target)
      }
    },
    // 绘制矩形
    drawRect(pointer) {
      if (!this.lastPoint || JSON.stringify(this.lastPoint) === JSON.stringify(pointer)) { // 点击事件,不生成矩形
        return
      }
      let top = Math.min(this.lastPoint.y, pointer.y)
      let left = Math.min(this.lastPoint.x, pointer.x)
      let width = Math.abs(this.lastPoint.x - pointer.x)
      let height = Math.abs(this.lastPoint.y - pointer.y)
      let rect = new fabric.Rect({ 
        top, 
        left,
        width,
        height,
        fill: 'transparent',
        stroke: this.strokeColor,
        selectable: false
      })
      this.canvas.add(rect)
      this.lastPoint = null
      this.strokeColor = 'transparent'
    },
    // 激活绘制矩形
    handleActiveRect() {
      this.isActiveRect = !this.isActiveRect
      if(this.isActiveRect) {
        this.canvas.selectionBorderColor = '#00FF64'
      }
    }
  }
}

二十一、自由绘制圆形

<template>
  <div class="canvas-box" ref="canvasBox">
    <canvas id="canvas"></canvas>
    <el-button @click="handleActiveCircle">绘制圆形</el-button>
  </div>
</template>
<script>
export default {
  name: 'PointerDetail',
  data () {
    return {
      canvas: null,
      canvasCircle: null,
      downPoint: null,
      strokeColor: 'transparent', // 轮廓填充颜色
      isActiveCircle: false, // 是否激活绘制圆形
      circleList: [] // 绘制的圆形列表
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    // 初始化
    init() {
      this.canvas = new fabric.Canvas('canvas', {
        width: this.$refs.canvasBox.offsetWidth,
        height: this.$refs.canvasBox.offsetHeight,
        backgroundColor: '#2E3136',
        selectionColor: 'transparent',
        selectionBorderColor: 'transparent',
        hoverCursor: 'default'
      })
      this.canvas.on('mouse:down', this.onMouseDown)
      this.canvas.on('mouse:move', this.onMouseMove)
      this.canvas.on('mouse:up', this.onMouseUp)
      this.canvas.on('object:added', this.onObjectAdded)
    },
    // 监听鼠标按下事件
    onMouseDown(opt) {
      if (this.isActiveCircle) {
        this.downPoint = opt.absolutePointer
        this.strokeColor = '#00FF64'
        this.canvasCircle = new fabric.Circle({
          top: this.downPoint.y,
          left: this.downPoint.x,
          radius: 0,
          fill: 'transparent',
          stroke: this.strokeColor,
          strokeWidth: 2,
          selectable: false,
        })
        this.canvas.add(this.canvasCircle)
      }
    },
    // 监听鼠标移动事件
    onMouseMove(opt) {
      if (this.isActiveCircle && this.canvasCircle) {
        let radius = Math.min(Math.abs(this.downPoint.x - opt.absolutePointer.x), Math.abs(this.downPoint.y - opt.absolutePointer.y)) / 2
        let top = opt.absolutePointer.y > this.downPoint.y ? this.downPoint.y : this.downPoint.y - radius * 2
        let left = opt.absolutePointer.x > this.downPoint.x ? this.downPoint.x :  this.downPoint.x - radius * 2
        this.canvasCircle.set('radius', radius)
        this.canvasCircle.set('top', top)
        this.canvasCircle.set('left', left)
        this.canvas.requestRenderAll()
      }
    },
    // 监听鼠标松开事件
    onMouseUp(opt) {
      if (this.isActiveCircle) {
        if (JSON.stringify(this.downPoint) === JSON.stringify(opt.absolutePointer)) {
          this.canvas.remove(this.canvasCircle)
        } else {
          if (this.canvasCircle){
            this.canvasCircle.set('stroke', this.strokeColor)
          }
        }
        this.canvasCircle = null
      }
    },
    // 绘制完成元素事件
    onObjectAdded(opt) {
      let target = opt.target
      if (target.stroke === '#00FF64') {
        this.isActiveCircle && this.circleList.push(target)
      }
    },
    // 激活绘制圆形
    handleActiveCircle() {
      this.isActiveCircle = !this.isActiveCircle
      if(this.isActiveCircle) {
        this.canvas.selectionBorderColor = '#00FF64'
      }
    }
  }
}

二十二、自由绘制椭圆形

<template>
  <div class="canvas-box" ref="canvasBox">
    <canvas id="canvas"></canvas>
    <el-button @click="handleActiveEllipse">绘制椭圆形</el-button>
  </div>
</template>
<script>
export default {
  name: 'PointerDetail',
  data () {
    return {
      canvas: null,
      canvasEllipse: null,
      downPoint: null,
      strokeColor: 'transparent', // 轮廓填充颜色
      isActiveEllipse: false, // 是否激活绘制椭圆形
      ellipseList: [] // 绘制的椭圆形列表
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    // 初始化
    init() {
      this.canvas = new fabric.Canvas('canvas', {
        width: this.$refs.canvasBox.offsetWidth,
        height: this.$refs.canvasBox.offsetHeight,
        backgroundColor: '#2E3136',
        selectionColor: 'transparent',
        selectionBorderColor: 'transparent',
        hoverCursor: 'default'
      })
      this.canvas.on('mouse:down', this.onMouseDown)
      this.canvas.on('mouse:move', this.onMouseMove)
      this.canvas.on('mouse:up', this.onMouseUp)
      this.canvas.on('object:added', this.onObjectAdded)
    },
    // 监听鼠标按下事件
    onMouseDown(opt) {
      if (this.isActiveEllipse) {
        this.downPoint = opt.absolutePointer
        this.strokeColor = '#00FF64'
        this.canvasEllipse = new fabric.Ellipse({
          top: this.downPoint.y,
          left: this.downPoint.x,
          rx: 0,
          ry: 0,
          fill: 'transparent',
          stroke: this.strokeColor,
          strokeWidth: 2,
          selectable: false,
        })
        this.canvas.add(this.canvasEllipse)
      }
    },
    // 监听鼠标移动事件
    onMouseMove(opt) {
      if (this.isActiveEllipse && this.canvasEllipse) {
        let rx = Math.abs(this.downPoint.x - opt.absolutePointer.x) / 2
        let ry = Math.abs(this.downPoint.y - opt.absolutePointer.y) / 2
        let top = opt.absolutePointer.y > this.downPoint.y ? this.downPoint.y : this.downPoint.y - ry * 2
        let left = opt.absolutePointer.x > this.downPoint.x ? this.downPoint.x :  this.downPoint.x - rx * 2
        this.canvasEllipse.set('rx', rx)
        this.canvasEllipse.set('ry', ry)
        this.canvasEllipse.set('top', top)
        this.canvasEllipse.set('left', left)
        this.canvas.requestRenderAll()
      }
    },
    // 监听鼠标松开事件
    onMouseUp(opt) {
      if (this.isActiveEllipse) {
        if (JSON.stringify(this.downPoint) === JSON.stringify(opt.absolutePointer)) {
          this.canvas.remove(this.canvasEllipse)
        } else {
          if (this.canvasEllipse){
            this.canvasEllipse.set('stroke', this.strokeColor)
          }
        }
        this.canvasEllipse = null
      }
    },
    // 绘制完成元素事件
    onObjectAdded(opt) {
      let target = opt.target
      if (target.stroke === '#00FF64') {
        this.isActiveEllipse && this.ellipseList.push(target)
      }
    },
    // 激活绘制椭圆形
    handleActiveEllipse() {
      this.isActiveEllipse = !this.isActiveEllipse
      if(this.isActiveEllipse) {
        this.canvas.selectionBorderColor = '#00FF64'
      }
    }
  }
}

二十三、自由绘制三角形

<template>
  <div class="canvas-box" ref="canvasBox">
    <canvas id="canvas"></canvas>
    <el-button @click="handleActiveTriangle">绘制三角形</el-button>
  </div>
</template>
<script>
export default {
  name: 'PointerDetail',
  data () {
    return {
      canvas: null,
      canvasTriangle: null,
      downPoint: null,
      strokeColor: 'transparent', // 轮廓填充颜色
      isActiveTriangle: false, // 是否激活绘制三角形
      triangleList: [] // 绘制的三角形列表
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    // 初始化
    init() {
      this.canvas = new fabric.Canvas('canvas', {
        width: this.$refs.canvasBox.offsetWidth,
        height: this.$refs.canvasBox.offsetHeight,
        backgroundColor: '#2E3136',
        selectionColor: 'transparent',
        selectionBorderColor: 'transparent',
        hoverCursor: 'default'
      })
      this.canvas.on('mouse:down', this.onMouseDown)
      this.canvas.on('mouse:move', this.onMouseMove)
      this.canvas.on('mouse:up', this.onMouseUp)
      this.canvas.on('object:added', this.onObjectAdded)
    },
    // 监听鼠标按下事件
    onMouseDown(opt) {
      if (this.isActiveTriangle) {
        this.downPoint = opt.absolutePointer
        this.strokeColor = '#00FF64'
        this.canvasTriangle = new fabric.Triangle({
          top: this.downPoint.y,
          left: this.downPoint.x,
          width: 0,
          height: 0,
          fill: 'transparent',
          stroke: this.strokeColor,
          strokeWidth: 2,
          selectable: false,
        })
        this.canvas.add(this.canvasTriangle)
      }
    },
    // 监听鼠标移动事件
    onMouseMove(opt) {
      if (this.isActiveTriangle && this.canvasTriangle) {
        let width = Math.abs(this.downPoint.x - opt.absolutePointer.x)
        let height = Math.abs(this.downPoint.y - opt.absolutePointer.y)
        let top = opt.absolutePointer.y > this.downPoint.y ? this.downPoint.y : opt.absolutePointer.y
        let left = opt.absolutePointer.x > this.downPoint.x ? this.downPoint.x : opt.absolutePointer.x
        this.canvasTriangle.set('width', width)
        this.canvasTriangle.set('height', height)
        this.canvasTriangle.set('top', top)
        this.canvasTriangle.set('left', left)
        this.canvas.requestRenderAll()
      }
    },
    // 监听鼠标松开事件
    onMouseUp(opt) {
      if (this.isActiveTriangle) {
        if (JSON.stringify(this.downPoint) === JSON.stringify(opt.absolutePointer)) {
          this.canvas.remove(this.canvasTriangle)
        } else {
          if (this.canvasTriangle){
            this.canvasTriangle.set('stroke', this.strokeColor)
          }
        }
        this.canvasTriangle = null
      }
    },
    // 绘制完成元素事件
    onObjectAdded(opt) {
      let target = opt.target
      if (target.stroke === '#00FF64') {
        this.isActiveTriangle && this.triangleList.push(target)
      }
    },
    // 激活绘制矩形
    handleActiveTriangle() {
      this.isActiveTriangle = !this.isActiveTriangle
      if(this.isActiveTriangle) {
        this.canvas.selectionBorderColor = '#00FF64'
      }
    }
  }
}

二十四、自由绘制多边形

<template>
  <div class="canvas-box" ref="canvasBox">
    <canvas id="canvas"></canvas>
    <el-button @click="handleActivePolygon">绘制多边形</el-button>
  </div>
</template>
<script>
export default {
  name: 'PointerDetail',
  data () {
    return {
      canvas: null,
      canvasPolygon: null,
      strokeColor: 'transparent', // 轮廓填充颜色
      isActivePolygon: false, // 是否激活绘制多边形
      polygonList: [] // 绘制的多边形列表
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    // 初始化
    init() {
      this.canvas = new fabric.Canvas('canvas', {
        width: this.$refs.canvasBox.offsetWidth,
        height: this.$refs.canvasBox.offsetHeight,
        backgroundColor: '#2E3136',
        selectionColor: 'transparent',
        selectionBorderColor: 'transparent',
        hoverCursor: 'default'
      })
      this.canvas.on('mouse:down', this.onMouseDown)
      this.canvas.on('mouse:move', this.onMouseMove)
      this.canvas.on('mouse:dblclick', this.onDblclick)
      this.canvas.on('object:added', this.onObjectAdded)
    },
    // 监听鼠标按下事件
    onMouseDown(opt) {
      if (this.isActivePolygon) {
        this.strokeColor = '#00FF64'
        if (this.canvasPolygon === null) {
          this.createPolygon(opt)
        } else {
          this.changeCurrentPolygon(opt)
        }
      }
    },
    // 监听鼠标移动事件
    onMouseMove(opt) {
      if (this.isActivePolygon && this.canvasPolygon) {
        this.changePolygonBelt(opt)
      }
    },
    // 鼠标双击事件
    onDblclick(opt) {
      this.finishPolygon(opt)
    },
    // 绘制完成元素事件
    onObjectAdded(opt) {
      let target = opt.target
      if (target.stroke === '#00FF64') {
        this.isActivePolygon && this.polygonList.push(target)
      }
    },
    // 创建多边形
    createPolygon(opt) {
      this.canvasPolygon = new fabric.Polygon([
          { x: opt.absolutePointer.x, y: opt.absolutePointer.y },
          { x: opt.absolutePointer.x, y: opt.absolutePointer.y }
        ], {
          fill: 'transparent',
          stroke: this.strokeColor,
          objectCaching: false
      })
      this.canvas.add(this.canvasPolygon)
    },
    // 修改当前正在创建的多边形
    changeCurrentPolygon(opt) {
      let points = this.canvasPolygon.points
      points.push({
        x: opt.absolutePointer.x,
        y: opt.absolutePointer.y
      })
      this.canvas.requestRenderAll()
    },
    // 多边形橡皮带
    changePolygonBelt(opt) {
      let points = this.canvasPolygon.points
      points[points.length - 1].x = opt.absolutePointer.x
      points[points.length - 1].y = opt.absolutePointer.y
      this.canvas.requestRenderAll()
    },
    // 完成多边形绘制
    finishPolygon(opt) {
      let points = this.canvasPolygon.points
      points[points.length - 1].x = opt.absolutePointer.x
      points[points.length - 1].y = opt.absolutePointer.y
      points.pop()
      points.pop()
      this.canvas.remove(this.canvasPolygon)
      if (points.length > 2) {
        let polygon = new fabric.Polygon(points, {
          stroke: this.strokeColor,
          fill: 'transparent',
          selectable: false
        })
        this.canvas.add(polygon)
      } else {
        this.$message.warning('标记框小于最小标定像素!')
      }
      this.canvasPolygon = null
      this.canvas.requestRenderAll()
      this.strokeColor = 'transparent'
    },
    // 激活绘制多边形
    handleActivePolygon() {
      this.isActivePolygon = !this.isActivePolygon
      if(this.isActivePolygon) {
        this.canvas.selectionBorderColor = '#00FF64'
      }
    }
  }
}

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

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

相关文章

高并发架构去重难?架构必备技能 - 布隆过滤器

系列文章目录 当Dubbo遇到高并发&#xff1a;探究流量控制解决方案 主从选举机制&#xff0c;架构高可用性的不二选择 高并发架构去重难&#xff1f;架构必备技能 - 布隆过滤器 系列文章目录前言一、布隆过滤器简介二、特性与应用场景三、参数定制四、java版本的Demo五、总结 …

安全学习DAY13_WEB应用源码获取

信息打点-WEB应用-源码获取 文章目录 信息打点-WEB应用-源码获取小节概述-思维导图资产架构-源码获取&#xff08;后端&#xff09;后端-开源后端-闭源-源码泄露源码泄露原因源码泄露方式集合网站备份压缩包git&#xff0c;svn源码泄露DS_Store文件泄露composer.json 泄露资源搜…

网络安全 Day24-select高级用法和多表连接

select高级用法和多表连接 1. select 多子句单表高级实践1.1 select 多子句高级语法1.2 聚合函数1.3 group by 实践1.4 having 筛选1.5 order by 排序1.6 limit 2. 多表连接 1. select 多子句单表高级实践 1.1 select 多子句高级语法 where 和 having 区别是后者是分组后进行…

计算机视觉实验:人脸识别系统设计

实验内容 设计计算机视觉目标识别系统&#xff0c;与实际应用有关&#xff08;建议&#xff1a;最终展示形式为带界面可运行的系统&#xff09;&#xff0c;以下内容选择其中一个做。 1. 人脸识别系统设计 (1) 人脸识别系统设计&#xff08;必做&#xff09;&#xff1a;根据…

【iOS】Cydia Impactor 错误:file http.hpp; line:37; what: _assert(code == 200)

Cydia Impactor 报错&#xff0c;信息如下 file http.hpp; line:37; what: _assert(code 200)解决方案&#xff1a;Cydia Impactor 已被弃用&#xff0c;切换到sideloadly 即可&#xff0c;亲测成功&#xff0c;并且支持双重验证登录 csdn备份地址 HERE

kotlin 编写一个简单的天气预报app(四)增加界面显示

编写界面来显示返回的数据 用户友好性&#xff1a;通过界面设计和用户体验优化&#xff0c;可以使天气信息更易读、易理解和易操作。有效的界面设计可以提高用户满意度并提供更好的交互体验。 增加城市名字的TextView <TextViewandroid:id"id/textViewCityName"…

华为OD机试 Java 实现【批量处理任务】【2023 B卷 200分】,二分查找

目录 专栏导读一、题目描述二、输入描述三、输出描述四、二分查找五、解题思路六、Java算法源码七、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;…

SPSS常见图表一览

SPSS是一个统计分析软件&#xff0c;而不是可视化分析工具&#xff0c;它输出的图表主要便于我们更好的理解输出结果&#xff0c;了解数据的基本分布形态。 因此&#xff0c;SPSS中的图表并不复杂&#xff0c;但不能说不重要&#xff0c;我们不需要花费太多时间深究&#xff0…

Postgresql源码(109)并行框架实例与分析

1 PostgreSQL并行参数 系统参数 系统总worker限制&#xff1a;max_worker_processes 默认8 系统总并发限制&#xff1a;max_parallel_workers 默认8 单Query限制&#xff1a;max_parallel_workers_per_gather 默认2 表参数限制&#xff1a;parallel_workers alter table tbl …

什么是 DNS ANAME 解析?

本人使用谷歌搜索了简中互联网&#xff0c;完全没有找到任何有关 ANAME 的文章……本文该不会是头一份吧 相信大家对于 DNS 的解析方式都不陌生&#xff0c;常见的有 A、CNAME、MX、TXT 记录等等。其中&#xff0c;网站常用的是 A 记录和 CNAME 记录&#xff1a;A 记录用于将域…

SQL Developer中的Active Data Guard

这篇文章 Display Data Guard configuration in SQL Developer 中&#xff0c;用SQL Developer展示了多种ADG的拓扑。 今天自己也试了一下&#xff0c;还蛮简单的&#xff0c;其实最麻烦的部分在于搭建一个ADG环境。 假设我已有一个ADG环境&#xff0c;即最典型的环境&#x…

数据库事务--数据库事务基本概念

2、认识事务 2.1、为什么需要事务 如何解决呢 使用事务 2.2、什么是事务 事务的概念: 数据库事务是访问并可能更新数据库中各种数据项的一个程序执行单元 事务的组成: 一个数据库事务通常包含对数据库进行读或写的的一个操作序列 事务的相关特性: 数据库事务可以包含一个或多…

EPICS通道访问介绍以及练习

提纲 1&#xff09; 通道访问概念 2&#xff09;通道访问API 3&#xff09; 简单的CA客户端 4&#xff09;使用回调的简单CA客户端 EPICS概要 搜索和连接过程 搜索请求 1&#xff09;搜索请求由一系列UDP包组成 只发送给EPICS_CA_ADDR_LIST从短时间间隔开始&#xff0c;每…

vue部署在iis的字体获取报错,请求404 - 找不到文件或目录

配置MIME即可 在添加MIME类型中&#xff0c;增加以下信息&#xff1a; 文件扩展名&#xff1a;.woff MIME类型&#xff1a;application/x-font-woff

DHorse v1.3.0 发布,基于k8s的发布平台

综述 DHorse是一个简单易用、以应用为中心的云原生DevOps系统&#xff0c;具有持续集成、持续部署、微服务治理等功能&#xff0c;无需安装依赖Docker、Maven、Node等环境即可发布Java、Vue、React应用&#xff0c;主要特点&#xff1a;部署简单、操作简洁、功能快速。 新增特…

《ChatGPT原理最佳解释,从根上理解ChatGPT》

【热点】 2022年11月30日&#xff0c;OpenAI发布ChatGPT&#xff08;全名&#xff1a;Chat Generative Pre-trained Transformer&#xff09;&#xff0c; 即聊天机器人程序 &#xff0c;开启AIGC的研究热潮。 ChatGPT是人工智能技术驱动的自然语言处理工具&#xff0c;它能够…

深入理解 SQL:从基本查询到高级聚合

目录 背景理论知识示例1211. 查询结果的质量和占比&#xff08;Round group by&#xff09;1204. 最后一个能进入巴士的人 &#xff08;Having limit order by&#xff09;1193. 每月交易 I&#xff08;if group by&#xff09;1179. 重新格式化部门表1174. 即时食物配送 II&am…

关于DC电源模块输入电压范围的问题

BOSHIDA 关于DC电源模块输入电压范围的问题 DC电源模块是一种将交流电转换为直流电的设备&#xff0c;它非常常见且广泛应用于电子设备、通讯设备、工业自动化等领域。而其输入电压范围也是我们在使用和选购DC电源模块时需要特别关注的一个参数。 首先&#xff0c;我们需要了解…

java的空引用null和空字符串““

java中如果字符串变量指向null&#xff0c;表示空引用&#xff0c;此时对字符串求长度会抛出异常。 而""表示一个空字符串&#xff0c;对字符串求长度是可以的&#xff0c;求出来的字符串长度为0。 举例&#xff1a; package com.thb;public class Test6 {public s…

数字电路(一)

1、例题 1、进行DA数模转换器选型时&#xff0c;一般要选择主要参数有&#xff08; A&#xff09;、转换精度和转换速度。 A、分辨率 B、输出电流 C、输出电阻 D、模拟开关 2、下图所示电路的逻辑功能为&#xff08; B&#xff09; A、与门 B、或门 C、与非门 D、或非门 分析该…