Optimize canvas highlight rendering

This commit is contained in:
Aljaž Grilc 2025-02-14 09:36:26 +01:00
parent d773682eeb
commit 6fb6503e73

View File

@ -279,7 +279,6 @@ class BesService {
// Scroll panel is "position: absolute", we need to keep it aligned with the host element.
this.scrollPanel.style.top = `${-this.hostElement.scrollTop}px`
this.scrollPanel.style.left = `${-this.hostElement.scrollLeft}px`
if (this.hostElement !== this.textElement) {
this.textElement.scrollTop = this.hostElement.scrollTop
this.textElement.scrollLeft = this.hostElement.scrollLeft
@ -332,44 +331,31 @@ class BesService {
*/
addMistakeMarkup(range, ruleId) {
const canvasPanelRect = this.canvasPanel.getBoundingClientRect()
console.log(canvasPanelRect)
const highlights = []
this.canvasPanel.classList.add('bes-canvas')
const ctx = this.canvasPanel.getContext('2d')
ctx.lineWidth = 2
ctx.lineWidth = 1
ctx.strokeStyle = ruleId.startsWith('MORFOLOGIK_RULE')
? '#e91313'
: '#269b26'
// Set the canvas dimensions and scale the context only once
if (!this.canvasInitialized) {
const dpr = window.devicePixelRatio || 1
this.canvasPanel.width = canvasPanelRect.width * dpr
this.canvasPanel.height = canvasPanelRect.height * dpr
this.canvasPanel.style.width = `${canvasPanelRect.width}px`
this.canvasPanel.style.height = `${canvasPanelRect.height}px`
const ctx = this.canvasPanel.getContext('2d')
ctx.scale(dpr, dpr)
this.canvasInitialized = true
}
this.setCanvasDpr()
for (let rect of range.getClientRects()) {
console.log(rect)
const x = rect.left - canvasPanelRect.left
const y = rect.top - canvasPanelRect.top + rect.height
const width = rect.width
const height = rect.height
console.log('rect.left: ', rect.left)
console.log('canvasPanelRect.left: ', canvasPanelRect.left)
console.log('x: ', x)
console.log('y: ', y)
console.log('width: ', width)
const globalX = rect.left + window.scrollX
const globalY = rect.top + window.scrollY
// Draw the underline
ctx.beginPath()
ctx.moveTo(x, y)
ctx.lineTo(x + width, y)
ctx.stroke()
const rectCoords = { x, y, width, height, globalX, globalY }
highlights.push(rectCoords)
}
return highlights
@ -410,7 +396,15 @@ class BesService {
* @returns
*/
static isPointInRect(x, y, rect) {
return rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom
const globalWidth = rect.globalX + rect.width
const globalHeight = rect.globalY + rect.height
return (
rect.globalX <= x &&
x < globalWidth &&
rect.globalY <= y &&
y < globalHeight
)
// return rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom
}
/**
@ -460,6 +454,7 @@ class BesService {
this.scrollPanel.style.height = `${this.hostElement.scrollHeight}px`
this.canvasPanel.style.width = `${this.hostElement.scrollWidth}px`
this.canvasPanel.style.height = `${this.hostElement.scrollHeight}px`
this.setCanvasDpr()
if (this.isHostElementInline()) {
const totalWidth =
parseFloat(styles.paddingLeft) +
@ -475,6 +470,18 @@ class BesService {
}
}
/**
* Sets canvas panel device pixel ratio.
*/
setCanvasDpr() {
const ctx = this.canvasPanel.getContext('2d')
const dpr = window.devicePixelRatio || 1
const canvasPanelRect = this.canvasPanel.getBoundingClientRect()
this.canvasPanel.width = canvasPanelRect.width * dpr
this.canvasPanel.height = canvasPanelRect.height * dpr
ctx.scale(dpr, dpr)
}
/**
* Displays correction panel.
*
@ -496,12 +503,12 @@ class BesService {
* @param {*} match Grammar checking rule match
*/
highlightMistake(match) {
document.querySelectorAll('.bes-mistake-highlight-selected').forEach(el => {
el.classList.remove('bes-mistake-highlight-selected')
})
match.highlights.forEach(h =>
h.classList.add('bes-mistake-highlight-selected')
)
// document.querySelectorAll('.bes-mistake-highlight-selected').forEach(el => {
// el.classList.remove('bes-mistake-highlight-selected')
// })
// match.highlights.forEach(h =>
// h.classList.add('bes-mistake-highlight-selected')
// )
}
/**
@ -522,9 +529,18 @@ class BesService {
* Updates all grammar mistake markup positions.
*/
repositionAllMarkup() {
const ctx = this.canvasPanel.getContext('2d')
this.results.forEach(result => {
result.matches.forEach(match => {
if (match.highlights) match.highlights.forEach(h => h.remove())
if (match.highlights) {
match.highlights.forEach(h => {
ctx.clearRect(h.x - 2, h.y - 2, h.width + 4, h.height)
})
delete match.highlights
// match.highlights.forEach(h => h.remove())
}
match.highlights = this.addMistakeMarkup(
match.range,
match.match.rule.id
@ -783,12 +799,20 @@ class BesTreeService extends BesService {
* @param {Element} el DOM element we want to clean markup for
*/
clearMarkup(el) {
const ctx = this.canvasPanel.getContext('2d')
this.results
.filter(result => BesTreeService.isSameParagraph(result.element, el))
.forEach(result =>
result.matches.forEach(match => {
console.log(match)
if (match.highlights) {
match.highlights.forEach(h => h.remove())
// match.highlights.forEach(h => h.remove())
match.highlights.forEach(h => {
console.log(h)
// Figure out why i need to add 2px to width
ctx.clearRect(h.x - 2, h.y - 2, h.width + 4, h.height)
})
delete match.highlights
}
})
@ -932,13 +956,7 @@ class BesTreeService extends BesService {
for (let result of this.results) {
for (let m of result.matches) {
for (let h of m.highlights) {
if (
BesService.isPointInRect(
source.clientX,
source.clientY,
h.getBoundingClientRect()
)
) {
if (BesService.isPointInRect(source.clientX, source.clientY, h)) {
this.popupCorrectionPanel(el, m, source)
return
}