Skip to content

Instantly share code, notes, and snippets.

@noih
Last active August 20, 2023 17:12
Show Gist options
  • Save noih/023d57ba27cc856273c261c5a079f3c7 to your computer and use it in GitHub Desktop.
Save noih/023d57ba27cc856273c261c5a079f3c7 to your computer and use it in GitHub Desktop.
PixiJS v7 - Prevent text flickering after scaling and achieve smoother rendering using batch updates
/**
* PixiJS v7 - Prevent text flickering after scaling and achieve smoother rendering using batch updates.
*
* @version 2
* @author noih.dev
* @since 2023-08-21
* @link https://codesandbox.io/s/batch-update-text-7qxktt?file=/src/index.mjs:0-3081
*/
import * as PIXI from 'pixi.js'
import { Viewport } from 'pixi-viewport'
import { Random } from 'random-js'
import Stats from 'stats.js'
const BatchSize = 150
const TextCount = 1000
const texts = []
let accumulator = 0
let lastScaled = 1
let isZooming = false
const app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
antialias: true,
autoDensity: true,
autoStart: false,
resolution: window.devicePixelRatio || 1,
backgroundColor: '0x383838',
resizeTo: window
})
document.body.appendChild(app.view)
app.ticker.stop()
const viewport = new Viewport({
screenWidth: window.innerWidth,
screenHeight: window.innerHeight,
worldWidth: 7000, // approximate
worldHeight: 7000,
divWheel: app.view,
ticker: app.ticker,
passiveWheel: false,
noTicker: true,
events: app.renderer.events
})
const container = new PIXI.Container()
const rnd = new Random()
const graphic = new PIXI.Graphics()
const style = new PIXI.TextStyle({
fontSize: 14,
fill: 0xff0000,
lineJoin: 'round',
stroke: '#000000',
// fontWeight: 'bold',
// strokeThickness: 1
})
// init
for (let i = 0; i < TextCount; i += 1) {
const t = new PIXI.Text(`測試測試測試\n測試測試\n${rnd.string(8)}`, style)
graphic.clear()
.beginTextureFill({ texture: t.texture })
.drawRect(0, 0, t.width, t.height)
.endFill()
const s = new PIXI.Sprite(app.renderer.generateTexture(graphic))
s.position.set((i % 40) * (t.width + 10), ~~(i / 40) * (t.height + 10))
s.roundPixels = true
s.cullable = true
texts.push([t, graphic, s])
container.addChild(s)
}
viewport.addChild(container)
app.stage.addChild(viewport)
viewport
.drag()
.wheel({ smooth: false, interrupt: true })
const update = () => {
if (isZooming || viewport.scaled === lastScaled) {
return
}
for (let i = accumulator; i < Math.min(accumulator + BatchSize, texts.length); i += 1) {
const [t, g, s] = texts[i]
t.style.fontSize = 14 * viewport.scaled
g.clear()
.beginTextureFill({ texture: t.texture })
.drawRect(0, 0, t.width, t.height)
.endFill()
s.texture.destroy() // destrpy previous
s.texture = app.renderer.generateTexture(g)
s.scale.set(1 / viewport.scaled)
}
accumulator += BatchSize
if (accumulator >= TextCount) {
lastScaled = viewport.scaled
}
}
viewport.on('zoomed', () => { isZooming = true })
viewport.on('zoomed-end',() => {
isZooming = false
accumulator = 0
})
// fps
const stats = new Stats()
stats.showPanel(0)
document.body.appendChild(stats.dom)
let prev = performance.now()
const loop = () => {
const now = performance.now()
const elapsedMS = now - prev
prev = now
app.renderer.render(app.stage)
viewport.update(elapsedMS * (60 / 1000))
update()
stats.update()
requestAnimationFrame(loop)
}
requestAnimationFrame(loop)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment