Мы работаем над веб-приложением для визуализации , которое использует d3-force для рисования сети на холсте.Поскольку каждый узел содержит много информации и поскольку он перезагружал все ресурсы для каждого кадра, мы реализовали своего рода кеш, где каждый узел рисуется на холсте (не связан с DOM), один раз для каждого уровня масштабирования.Затем эти полотна рисуются на главном холсте (связанном с DOM) в позиции узла.
Мы довольны увеличением скорости, даже если результат теперь требует большого объема памяти (особенно на дисплеях хидпи (retina), где плотность пикселей может быть 2 или 3).
Но теперь у нас возникла проблема с браузерами на iOS, где процесс завершается сбоем после нескольких взаимодействий с интерфейсом.Насколько я помню, это не было проблемой со старой версией (до iOS12), но у меня нет ни одного не обновленного устройства, чтобы подтвердить это.
Я думаю, что этот код суммирует проблему:
const { range } = require('d3-array')
// create a 1MB image
const createImage = () => {
const size = 512
const canvas = document.createElement('canvas')
canvas.height = size
canvas.width = size
const ctx = canvas.getContext('2d')
ctx.strokeRect(0, 0, size, size)
return canvas
}
const createImages = i => {
// create i * 1MB images
let ctxs = range(i).map(() => {
return createImage()
})
console.log(`done for ${ctxs.length} MB`)
ctxs = null
}
window.cis = createImages
Затем на iPad и в инспекторе:
> cis(256)
[Log] done for 256 MB (main-a9168dc888c2e24bbaf3.bundle.js, line 11317)
< undefined
> cis(1)
[Warning] Total canvas memory use exceeds the maximum limit (256 MB). (main-a9168dc888c2e24bbaf3.bundle.js, line 11307)
< TypeError: null is not an object (evaluating 'ctx.strokeRect')
Я создаю холст размером 256 x 1 МБ, все идет хорошо, но я создаю еще один, и canvas.getContext возвращаетнулевой указатель.Тогда невозможно создать еще один холст.
Похоже, что ограничение связано с устройством, поскольку на iPad оно составляет 256 МБ, а на iPhone X - 288 МБ.
> cis(288)
[Log] done for 288 MB (main-a9168dc888c2e24bbaf3.bundle.js, line 11317)
< undefined
> cis(1)
[Warning] Total canvas memory use exceeds the maximum limit (288 MB). (main-a9168dc888c2e24bbaf3.bundle.js, line 11307)
< TypeError: null is not an object (evaluating 'ctx.strokeRect')
Как оно естькеш, я должен иметь возможность удалять некоторые элементы, но это не так (поскольку установка ctxs или ctx в ноль должна вызывать GC, но это не решает проблему).
Единственной релевантной страницей, которую я нашел по этой проблеме, является исходная кодовая страница webkit: HTMLCanvasElement.cpp .
Я подозреваю, что проблема может исходить от самого webkit, но яЯ хотел бы убедиться перед публикацией в трекере проблем с веб-наборами.
Есть ли другой способ уничтожить контексты холста?
Заранее спасибо за любую идею, указатель, ...
РЕДАКТИРОВАТЬ:
Чтобы добавить некоторую информацию, я пробовал другие браузеры.Safari 12 имеет ту же проблему на macOS, даже если предел выше (1/4 памяти компьютера, как указано в источниках webkit).Я также попытался с последней сборкой webkit (236590) без особой удачи.Но код работает на Firefox 62 и Chrome 69.
Я усовершенствовал тестовый код, чтобы его можно было выполнить непосредственно из консоли отладчика.Было бы очень полезно, если бы кто-то смог протестировать код на старом сафари (например, 11).
let counter = 0
// create a 1MB image
const createImage = () => {
const size = 512
const canvas = document.createElement('canvas')
canvas.height = size
canvas.width = size
const ctx = canvas.getContext('2d')
ctx.strokeRect(0, 0, size, size)
return canvas
}
const createImages = n => {
// create n * 1MB images
const ctxs = []
for( let i=0 ; i<n ; i++ ){
ctxs.push(createImage())
}
console.log(`done for ${ctxs.length} MB`)
}
const process = (frequency,size) => {
setInterval(()=>{
createImages(size)
counter+=size
console.log(`total ${counter}`)
},frequency)
}
process(2000,1000)