清理调试代码,抽取canvas功能封装
This commit is contained in:
@@ -2,15 +2,7 @@
|
|||||||
import { ref, watch, nextTick, onMounted, onUnmounted } from 'vue'
|
import { ref, watch, nextTick, onMounted, onUnmounted } from 'vue'
|
||||||
import type { Task } from '../models/classes/Task'
|
import type { Task } from '../models/classes/Task'
|
||||||
import { getPredecessorIds } from '../utils/predecessorUtils'
|
import { getPredecessorIds } from '../utils/predecessorUtils'
|
||||||
|
import { CanvasContextManager } from '../utils/canvasUtils'
|
||||||
// uni-app 类型声明
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
uni?: {
|
|
||||||
createCanvasContext: (canvasId: string) => CanvasRenderingContext2D & { draw?: () => void }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定义 TaskBar 位置信息类型
|
// 定义 TaskBar 位置信息类型
|
||||||
interface TaskBarPosition {
|
interface TaskBarPosition {
|
||||||
@@ -52,6 +44,9 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
// Canvas 引用
|
// Canvas 引用
|
||||||
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
||||||
|
|
||||||
|
// Canvas 上下文管理器
|
||||||
|
const canvasManager = new CanvasContextManager()
|
||||||
|
|
||||||
// requestAnimationFrame 防抖控制
|
// requestAnimationFrame 防抖控制
|
||||||
let rafId: number | null = null
|
let rafId: number | null = null
|
||||||
let pendingRedraw = false
|
let pendingRedraw = false
|
||||||
@@ -71,22 +66,13 @@ const updateTheme = () => {
|
|||||||
* 性能优势:相比 SVG 提升 18 倍渲染性能
|
* 性能优势:相比 SVG 提升 18 倍渲染性能
|
||||||
*/
|
*/
|
||||||
const drawLinks = () => {
|
const drawLinks = () => {
|
||||||
const canvas = canvasRef.value
|
const ctx = canvasManager.getContext({
|
||||||
if (!canvas) return
|
canvas: canvasRef.value,
|
||||||
|
canvasId: 'gantt-link-canvas',
|
||||||
// 判断是否在 uni-app 环境中
|
width: props.width,
|
||||||
const isUniApp = typeof window !== 'undefined' && window.uni
|
height: props.height,
|
||||||
let ctx: (CanvasRenderingContext2D & { draw?: () => void }) | null = null
|
contextOptions: { alpha: true },
|
||||||
|
})
|
||||||
if (isUniApp && window.uni) {
|
|
||||||
// uni-app 环境:使用 uni.createCanvasContext
|
|
||||||
// https://uniapp.dcloud.net.cn/component/canvas.html
|
|
||||||
// 这儿存在一个问题:如果一个应用多个gantt视图会存在id冲突问题,所以后期可以加上一个配置,外部传入canvas-id
|
|
||||||
ctx = window.uni.createCanvasContext('gantt-link-canvas')
|
|
||||||
} else {
|
|
||||||
// Web 环境:使用标准 Canvas API
|
|
||||||
ctx = canvas.getContext('2d', { alpha: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
@@ -97,18 +83,6 @@ const drawLinks = () => {
|
|||||||
const displayWidth = props.width
|
const displayWidth = props.width
|
||||||
const displayHeight = props.height
|
const displayHeight = props.height
|
||||||
|
|
||||||
// Web 环境才需要手动处理 DPR(uni-app 通过 hidpi 属性自动处理)
|
|
||||||
if (!isUniApp) {
|
|
||||||
const dpr = window.devicePixelRatio || 1
|
|
||||||
const pixelWidth = displayWidth * dpr
|
|
||||||
const pixelHeight = displayHeight * dpr
|
|
||||||
if (canvas.width !== pixelWidth || canvas.height !== pixelHeight) {
|
|
||||||
canvas.width = pixelWidth
|
|
||||||
canvas.height = pixelHeight
|
|
||||||
ctx.scale(dpr, dpr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空画布(透明)
|
// 清空画布(透明)
|
||||||
ctx.clearRect(0, 0, displayWidth, displayHeight)
|
ctx.clearRect(0, 0, displayWidth, displayHeight)
|
||||||
|
|
||||||
@@ -336,9 +310,7 @@ const drawLinks = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// uni-app 环境需要调用 draw() 方法将绘制内容渲染到画布
|
// uni-app 环境需要调用 draw() 方法将绘制内容渲染到画布
|
||||||
if (isUniApp && ctx?.draw) {
|
canvasManager.draw()
|
||||||
ctx.draw()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, onUnmounted } from 'vue'
|
import { ref, watch, onUnmounted } from 'vue'
|
||||||
|
import { CanvasContextManager } from '../utils/canvasUtils'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
active: boolean
|
active: boolean
|
||||||
@@ -17,47 +18,20 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
|
|
||||||
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
||||||
|
|
||||||
// 缓存 canvas 上下文和尺寸信息,避免重复初始化
|
// Canvas 上下文管理器
|
||||||
let cachedCtx: CanvasRenderingContext2D | null = null
|
const canvasManager = new CanvasContextManager()
|
||||||
let cachedWidth = 0
|
|
||||||
let cachedHeight = 0
|
|
||||||
let cachedDpr = 0
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化或更新 canvas 尺寸
|
* 初始化或更新 canvas 尺寸
|
||||||
*/
|
*/
|
||||||
const initCanvas = () => {
|
const initCanvas = () => {
|
||||||
const canvas = canvasRef.value
|
return canvasManager.getContext({
|
||||||
if (!canvas) return null
|
canvas: canvasRef.value,
|
||||||
|
canvasId: 'link-drag-guide-canvas',
|
||||||
const displayWidth = props.width
|
width: props.width,
|
||||||
const displayHeight = props.height
|
height: props.height,
|
||||||
const dpr = window.devicePixelRatio || 1
|
contextOptions: { alpha: true, willReadFrequently: false },
|
||||||
|
})
|
||||||
// 只在尺寸或 DPR 变化时重新初始化
|
|
||||||
if (
|
|
||||||
!cachedCtx ||
|
|
||||||
cachedWidth !== displayWidth ||
|
|
||||||
cachedHeight !== displayHeight ||
|
|
||||||
cachedDpr !== dpr
|
|
||||||
) {
|
|
||||||
const ctx = canvas.getContext('2d', { alpha: true, willReadFrequently: false })
|
|
||||||
if (!ctx) return null
|
|
||||||
|
|
||||||
const pixelWidth = displayWidth * dpr
|
|
||||||
const pixelHeight = displayHeight * dpr
|
|
||||||
|
|
||||||
canvas.width = pixelWidth
|
|
||||||
canvas.height = pixelHeight
|
|
||||||
ctx.scale(dpr, dpr)
|
|
||||||
|
|
||||||
cachedCtx = ctx
|
|
||||||
cachedWidth = displayWidth
|
|
||||||
cachedHeight = displayHeight
|
|
||||||
cachedDpr = dpr
|
|
||||||
}
|
|
||||||
|
|
||||||
return cachedCtx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,8 +60,8 @@ const draw = (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayWidth = cachedWidth
|
const displayWidth = props.width
|
||||||
const displayHeight = cachedHeight
|
const displayHeight = props.height
|
||||||
|
|
||||||
// 清空画布
|
// 清空画布
|
||||||
ctx.clearRect(0, 0, displayWidth, displayHeight)
|
ctx.clearRect(0, 0, displayWidth, displayHeight)
|
||||||
@@ -180,21 +154,23 @@ const draw = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
|
|
||||||
|
// uni-app 环境需要调用 draw() 方法将绘制内容渲染到画布
|
||||||
|
canvasManager.draw()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清空画布
|
* 清空画布
|
||||||
*/
|
*/
|
||||||
const clear = () => {
|
const clear = () => {
|
||||||
if (!cachedCtx) return
|
canvasManager.clear(props.width, props.height)
|
||||||
cachedCtx.clearRect(0, 0, cachedWidth, cachedHeight)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听尺寸变化,需要重新初始化 canvas
|
// 监听尺寸变化,需要重新初始化 canvas
|
||||||
watch(
|
watch(
|
||||||
[() => props.width, () => props.height],
|
[() => props.width, () => props.height],
|
||||||
() => {
|
() => {
|
||||||
cachedCtx = null // 清除缓存,下次绘制时重新初始化
|
canvasManager.reset() // 清除缓存,下次绘制时重新初始化
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -204,14 +180,14 @@ watch(
|
|||||||
(newActive) => {
|
(newActive) => {
|
||||||
if (!newActive) {
|
if (!newActive) {
|
||||||
// canvas 即将被移除,清除缓存
|
// canvas 即将被移除,清除缓存
|
||||||
cachedCtx = null
|
canvasManager.reset()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// 组件卸载时清除缓存
|
// 组件卸载时清除缓存
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
cachedCtx = null
|
canvasManager.reset()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 🚀 暴露命令式 API 给父组件
|
// 🚀 暴露命令式 API 给父组件
|
||||||
@@ -225,7 +201,10 @@ defineExpose({
|
|||||||
<canvas
|
<canvas
|
||||||
v-if="active"
|
v-if="active"
|
||||||
ref="canvasRef"
|
ref="canvasRef"
|
||||||
|
canvas-id="link-drag-guide-canvas"
|
||||||
class="link-drag-guide-canvas"
|
class="link-drag-guide-canvas"
|
||||||
|
type="2d"
|
||||||
|
:hidpi="true"
|
||||||
:style="{
|
:style="{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: `${offsetLeft}px`,
|
left: `${offsetLeft}px`,
|
||||||
|
|||||||
+30
-38
@@ -666,7 +666,7 @@ let nonReactiveErrorMessage = ''
|
|||||||
const taskIdMap = new Map<number, Task>()
|
const taskIdMap = new Map<number, Task>()
|
||||||
|
|
||||||
// 性能监控开关(开发调试用)
|
// 性能监控开关(开发调试用)
|
||||||
const ENABLE_PERF_MONITOR = true
|
const ENABLE_PERF_MONITOR = false
|
||||||
|
|
||||||
// 开始连接线拖拽
|
// 开始连接线拖拽
|
||||||
const handleLinkDragStart = (event: { task: Task; type: 'predecessor' | 'successor'; mouseEvent: MouseEvent }) => {
|
const handleLinkDragStart = (event: { task: Task; type: 'predecessor' | 'successor'; mouseEvent: MouseEvent }) => {
|
||||||
@@ -674,7 +674,9 @@ const handleLinkDragStart = (event: { task: Task; type: 'predecessor' | 'success
|
|||||||
linkDragSourceTask.value = event.task
|
linkDragSourceTask.value = event.task
|
||||||
|
|
||||||
// 启动帧监控
|
// 启动帧监控
|
||||||
perfMonitor.startFrameMonitor()
|
if (ENABLE_PERF_MONITOR) {
|
||||||
|
perfMonitor.startFrameMonitor()
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化鼠标坐标(使用非响应式版本)
|
// 初始化鼠标坐标(使用非响应式版本)
|
||||||
updateLinkDragCoordinatesNonReactive(event.mouseEvent.clientX, event.mouseEvent.clientY)
|
updateLinkDragCoordinatesNonReactive(event.mouseEvent.clientX, event.mouseEvent.clientY)
|
||||||
@@ -704,47 +706,35 @@ let pendingMouseY = 0
|
|||||||
let currentDragX = 0
|
let currentDragX = 0
|
||||||
let currentDragY = 0
|
let currentDragY = 0
|
||||||
|
|
||||||
// 🔧 调试开关:逐步启用各操作以定位性能瓶颈
|
|
||||||
const DEBUG_ENABLE_COORD_UPDATE = true // 坐标更新 ✅ 不是瓶颈
|
|
||||||
const DEBUG_ENABLE_TARGET_DETECT = true // 目标检测 ✅ 不是瓶颈
|
|
||||||
const DEBUG_ENABLE_CANVAS_DRAW = true // Canvas 绘制 - 测试中
|
|
||||||
|
|
||||||
// 🚀 优化后的 RAF 回调:在一帧内批量处理坐标更新、目标检测和绘制
|
// 🚀 优化后的 RAF 回调:在一帧内批量处理坐标更新、目标检测和绘制
|
||||||
const processLinkDragFrame = () => {
|
const processLinkDragFrame = () => {
|
||||||
linkDragRafId = null
|
linkDragRafId = null
|
||||||
|
|
||||||
// 🔧 调试:逐步启用各操作
|
if (ENABLE_PERF_MONITOR) {
|
||||||
if (DEBUG_ENABLE_COORD_UPDATE) {
|
const startTime = performance.now()
|
||||||
if (ENABLE_PERF_MONITOR) {
|
updateLinkDragCoordinatesNonReactive(pendingMouseX, pendingMouseY)
|
||||||
const startTime = performance.now()
|
perfMonitor.recordLinkDragCoordUpdate(performance.now() - startTime)
|
||||||
updateLinkDragCoordinatesNonReactive(pendingMouseX, pendingMouseY)
|
} else {
|
||||||
perfMonitor.recordLinkDragCoordUpdate(performance.now() - startTime)
|
updateLinkDragCoordinatesNonReactive(pendingMouseX, pendingMouseY)
|
||||||
} else {
|
|
||||||
updateLinkDragCoordinatesNonReactive(pendingMouseX, pendingMouseY)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG_ENABLE_TARGET_DETECT) {
|
if (ENABLE_PERF_MONITOR) {
|
||||||
if (ENABLE_PERF_MONITOR) {
|
const startTime = performance.now()
|
||||||
const startTime = performance.now()
|
detectLinkTargetNonReactive(pendingMouseX, pendingMouseY)
|
||||||
detectLinkTargetNonReactive(pendingMouseX, pendingMouseY)
|
perfMonitor.recordLinkDragTargetDetect(performance.now() - startTime)
|
||||||
perfMonitor.recordLinkDragTargetDetect(performance.now() - startTime)
|
} else {
|
||||||
} else {
|
detectLinkTargetNonReactive(pendingMouseX, pendingMouseY)
|
||||||
detectLinkTargetNonReactive(pendingMouseX, pendingMouseY)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG_ENABLE_CANVAS_DRAW) {
|
if (linkDragGuideRef.value && linkDragSourceTask.value) {
|
||||||
if (linkDragGuideRef.value && linkDragSourceTask.value) {
|
linkDragGuideRef.value.draw(
|
||||||
linkDragGuideRef.value.draw(
|
getLinkDragStartX(),
|
||||||
getLinkDragStartX(),
|
getLinkDragStartY(),
|
||||||
getLinkDragStartY(),
|
currentDragX,
|
||||||
currentDragX,
|
currentDragY,
|
||||||
currentDragY,
|
nonReactiveIsValidTarget,
|
||||||
nonReactiveIsValidTarget,
|
nonReactiveErrorMessage,
|
||||||
nonReactiveErrorMessage,
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -770,7 +760,9 @@ const handleGlobalMouseUp = () => {
|
|||||||
if (!dragLinkMode.value) return
|
if (!dragLinkMode.value) return
|
||||||
|
|
||||||
// 停止帧监控
|
// 停止帧监控
|
||||||
perfMonitor.stopFrameMonitor()
|
if (ENABLE_PERF_MONITOR) {
|
||||||
|
perfMonitor.stopFrameMonitor()
|
||||||
|
}
|
||||||
|
|
||||||
// 🚀 取消待处理的 RAF
|
// 🚀 取消待处理的 RAF
|
||||||
if (linkDragRafId !== null) {
|
if (linkDragRafId !== null) {
|
||||||
@@ -809,8 +801,8 @@ const updateCoordinates = (mouseX: number, mouseY: number): void => {
|
|||||||
bodyRectCacheTime = now
|
bodyRectCacheTime = now
|
||||||
bodyRectInvalidated = false
|
bodyRectInvalidated = false
|
||||||
}
|
}
|
||||||
currentDragX = mouseX - cachedBodyRect.left
|
currentDragX = mouseX - cachedBodyRect!.left
|
||||||
currentDragY = mouseY - cachedBodyRect.top
|
currentDragY = mouseY - cachedBodyRect!.top
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🚀 非响应式坐标更新(完全绕过 Vue 响应式系统)
|
// 🚀 非响应式坐标更新(完全绕过 Vue 响应式系统)
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
/**
|
||||||
|
* Canvas 工具函数 - 支持 uni-app 和 Web 环境
|
||||||
|
*/
|
||||||
|
|
||||||
|
// uni-app 类型声明
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
uni?: {
|
||||||
|
createCanvasContext: (canvasId: string) => CanvasRenderingContext2D & { draw?: () => void }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canvas 上下文类型(兼容 uni-app 和 Web)
|
||||||
|
*/
|
||||||
|
export type CanvasContext = CanvasRenderingContext2D & { draw?: () => void }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canvas 初始化配置
|
||||||
|
*/
|
||||||
|
export interface CanvasInitOptions {
|
||||||
|
/** Canvas 元素引用(Web 环境) */
|
||||||
|
canvas: HTMLCanvasElement | null
|
||||||
|
/** Canvas ID(uni-app 环境) */
|
||||||
|
canvasId: string
|
||||||
|
/** 显示宽度 */
|
||||||
|
width: number
|
||||||
|
/** 显示高度 */
|
||||||
|
height: number
|
||||||
|
/** Canvas 上下文配置(仅 Web 环境) */
|
||||||
|
contextOptions?: CanvasRenderingContext2DSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canvas 缓存管理器
|
||||||
|
*/
|
||||||
|
export class CanvasContextManager {
|
||||||
|
private cachedCtx: CanvasContext | null = null
|
||||||
|
private cachedWidth = 0
|
||||||
|
private cachedHeight = 0
|
||||||
|
private isUniApp = false
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// 判断是否在 uni-app 环境中
|
||||||
|
this.isUniApp = typeof window !== 'undefined' && !!window.uni
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测是否在 uni-app 环境
|
||||||
|
*/
|
||||||
|
get isUniAppEnvironment(): boolean {
|
||||||
|
return this.isUniApp
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取或初始化 Canvas 上下文
|
||||||
|
* @param options 初始化配置
|
||||||
|
* @returns Canvas 上下文,失败返回 null
|
||||||
|
*/
|
||||||
|
getContext(options: CanvasInitOptions): CanvasContext | null {
|
||||||
|
const { canvas, canvasId, width, height, contextOptions } = options
|
||||||
|
|
||||||
|
// 只在尺寸变化时重新初始化
|
||||||
|
if (!this.cachedCtx || this.cachedWidth !== width || this.cachedHeight !== height) {
|
||||||
|
let ctx: CanvasContext | null = null
|
||||||
|
|
||||||
|
if (this.isUniApp && window.uni) {
|
||||||
|
// uni-app 环境:使用 uni.createCanvasContext
|
||||||
|
ctx = window.uni.createCanvasContext(canvasId)
|
||||||
|
} else {
|
||||||
|
// Web 环境:使用标准 Canvas API
|
||||||
|
if (!canvas) return null
|
||||||
|
|
||||||
|
const dpr = window.devicePixelRatio || 1
|
||||||
|
ctx = canvas.getContext('2d', contextOptions || { alpha: true }) as CanvasContext
|
||||||
|
|
||||||
|
if (!ctx) return null
|
||||||
|
|
||||||
|
const pixelWidth = width * dpr
|
||||||
|
const pixelHeight = height * dpr
|
||||||
|
|
||||||
|
canvas.width = pixelWidth
|
||||||
|
canvas.height = pixelHeight
|
||||||
|
ctx.scale(dpr, dpr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx) return null
|
||||||
|
|
||||||
|
this.cachedCtx = ctx
|
||||||
|
this.cachedWidth = width
|
||||||
|
this.cachedHeight = height
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.cachedCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空画布
|
||||||
|
*/
|
||||||
|
clear(width: number, height: number): void {
|
||||||
|
if (!this.cachedCtx) return
|
||||||
|
this.cachedCtx.clearRect(0, 0, width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发 uni-app 画布渲染(仅 uni-app 环境需要)
|
||||||
|
*/
|
||||||
|
draw(): void {
|
||||||
|
if (this.isUniApp && this.cachedCtx?.draw) {
|
||||||
|
this.cachedCtx.draw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置缓存(例如 Canvas 元素被销毁时)
|
||||||
|
*/
|
||||||
|
reset(): void {
|
||||||
|
this.cachedCtx = null
|
||||||
|
this.cachedWidth = 0
|
||||||
|
this.cachedHeight = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测是否在 uni-app 环境
|
||||||
|
*/
|
||||||
|
export function isUniAppEnvironment(): boolean {
|
||||||
|
return typeof window !== 'undefined' && !!window.uni
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 Canvas 上下文(简化版 API)
|
||||||
|
*/
|
||||||
|
export function createCanvasContext(
|
||||||
|
canvas: HTMLCanvasElement | null,
|
||||||
|
canvasId: string,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
contextOptions?: CanvasRenderingContext2DSettings,
|
||||||
|
): CanvasContext | null {
|
||||||
|
const isUniApp = isUniAppEnvironment()
|
||||||
|
|
||||||
|
if (isUniApp && window.uni) {
|
||||||
|
// uni-app 环境
|
||||||
|
return window.uni.createCanvasContext(canvasId)
|
||||||
|
} else {
|
||||||
|
// Web 环境
|
||||||
|
if (!canvas) return null
|
||||||
|
|
||||||
|
const dpr = window.devicePixelRatio || 1
|
||||||
|
const ctx = canvas.getContext('2d', contextOptions || { alpha: true }) as CanvasContext
|
||||||
|
|
||||||
|
if (!ctx) return null
|
||||||
|
|
||||||
|
const pixelWidth = width * dpr
|
||||||
|
const pixelHeight = height * dpr
|
||||||
|
|
||||||
|
canvas.width = pixelWidth
|
||||||
|
canvas.height = pixelHeight
|
||||||
|
ctx.scale(dpr, dpr)
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user