Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/404.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 画布内存使用总量超过最大限制(Safari 12)_Javascript_Safari_Html5 Canvas_Webkit_Mobile Safari - Fatal编程技术网

Javascript 画布内存使用总量超过最大限制(Safari 12)

Javascript 画布内存使用总量超过最大限制(Safari 12),javascript,safari,html5-canvas,webkit,mobile-safari,Javascript,Safari,Html5 Canvas,Webkit,Mobile Safari,我们正在研究一种使用d3力在画布上绘制网络的方法 但现在我们在iOS上遇到了浏览器的问题,在与界面进行几次交互后,进程崩溃。 据我回忆,旧版本(iOS12之前)没有问题,但我没有任何未更新的设备来证实这一点 我认为这段代码总结了问题: const { range } = require('d3-array') // create a 1MB image const createImage = () => { const size = 512 const canvas =

我们正在研究一种使用d3力在画布上绘制网络的方法

但现在我们在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和inspector中:

> 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')
>cis(256)
[日志]完成256 MB(main-a9168dc888c2e24bbaf3.bundle.js,第11317行)
<未定义
>独联体(1)
[警告]画布内存使用总量超过最大限制(256 MB)。(main-a9168dc888c2e24bbaf3.bundle.js,第11307行)
因此,我创建了256 x 1MB画布,一切都进行得很顺利,但我又创建了一个画布,canvas.getContext返回一个空指针。 这样就不可能创建另一个画布

这一限制似乎与设备有关,因为在iPad上是256MB,而在iPhoneX上是288MB

> 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')
>cis(288)
[日志]完成288 MB(main-a9168dc888c2e24bbaf3.bundle.js,第11317行)
<未定义
>独联体(1)
[警告]画布内存使用总量超过最大限制(288 MB)。(main-a9168dc888c2e24bbaf3.bundle.js,第11307行)
由于它是一个缓存,我应该能够删除一些元素,但我不能(因为将ctxs或ctx设置为null应该会触发GC,但它不能解决问题)

我在此问题上找到的唯一相关页面是webkit源代码页:

我怀疑问题可能来自webkit本身,但我想在发布到webkit问题跟踪器之前确定一下

有没有其他方法可以破坏画布上下文

提前感谢您的任何想法,指针

更新

我发现这个Webkit问题(可能)是对这个bug的描述:

为了添加一些信息,我尝试了其他浏览器。Safari 12在macOS上也有同样的问题,即使限制更高(如webkit来源中所述,占计算机内存的1/4)。我还尝试了最新的webkit版本(236590),但运气不好。 但该代码在Firefox 62和Chrome 69上运行

我改进了测试代码,因此可以直接从调试器控制台执行。如果有人能在较旧的safari(如11)上测试代码,这将非常有帮助

let计数器=0
//创建1MB图像
常量createImage=()=>{
常数大小=512
const canvas=document.createElement('canvas')
canvas.height=大小
画布宽度=大小
const ctx=canvas.getContext('2d')
ctx.strokeRect(0,0,大小,大小)
返回画布
}
const createImages=n=>{
//创建n*1MB图像
常量ctxs=[]
for(设i=0;i{
设置间隔(()=>{
创建图像(大小)
计数器+=大小
log(`total${counter}`)
},频率)
}
工艺(20001000)

有人发布了一个答案,显示了一个解决方法。这个想法是在删除画布之前将高度和宽度设置为0。这不是一个真正合适的解决方案,但它可以在我的缓存系统中工作

我添加了一个小示例,它创建画布直到抛出异常,然后清空缓存并继续

感谢发布此答案的匿名人士

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 = nbImage => {
    // create i * 1MB images
    const canvases = []

    for (let i = 0; i < nbImage; i++) {
        canvases.push(createImage())
    }

    console.log(`done for ${canvases.length} MB`)
    return canvases
}

const deleteCanvases = canvases => {
    canvases.forEach((canvas, i, a) => {
        canvas.height = 0
        canvas.width = 0
    })
}

let canvases = []
const process = (frequency, size) => {
    setInterval(() => {
        try {
            canvases.push(...createImages(size))
            counter += size
            console.log(`total ${counter}`)
        }
        catch (e) {
            deleteCanvases(canvases)
            canvases = []
        }
    }, frequency)
}


process(2000, 1000)
let计数器=0
//创建1MB图像
常量createImage=()=>{
常数大小=512
const canvas=document.createElement('canvas')
canvas.height=大小
画布宽度=大小
const ctx=canvas.getContext('2d')
ctx.strokeRect(0,0,大小,大小)
返回画布
}
const createImages=nbImage=>{
//创建i*1MB图像
常量画布=[]
for(设i=0;i{
画布.forEach((画布,i,a)=>{
canvas.height=0
canvas.width=0
})
}
让画布=[]
常量进程=(频率、大小)=>{
设置间隔(()=>{
试一试{
画布。推送(…创建图像(大小))
计数器+=大小
log(`total${counter}`)
}
捕获(e){
删除画布(画布)
画布=[]
}
},频率)
}
过程(2000、1000)

我花了一个周末制作了一个简单的网页,可以快速显示问题。我已经向谷歌和苹果提交了错误报告。该网页会显示一张地图。你可以随意平移和缩放,Safari inspector(在iPad上运行web,使用MacBook Pro查看画布)看不到画布

然后,你可以点击一个按钮并绘制一条多段线。当你这样做时,你会看到41张画布。平移或缩放,你会看到更多。每个画布都是1MB,因此在你有256张孤立画布后,错误会随着iPad上画布内存的满而开始

重新加载页面,点击按钮放置一个多边形,同样的事情也会发生

同样有趣的是,我添加了按钮来设置白天和晚上的地图样式。当它只是一张地图(或者一张只有标记的地图,有一个按钮可以在地图上显示一些标记)时,你可以来回移动。没有孤立的画布。但是画一条线,当你改变样式时,你会看到更多孤立的画布

在活动监视器中查看MacBook上的Safari,绘制多边形后,在地图周围平移和缩放时,大小一直在变化*


我希望苹果和谷歌能够解决这个问题,而不是说这是另一家公司的问题。随着IOS12运行多年稳定的网页,所有这些都发生了变化。我一直在测试IOS 9和10 iPad,以确保旧设备可以显示当前的网页。希望这个测试/实验能有所帮助。

可能吧WebKit最近的变化应该会导致这些问题 我能
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 = nbImage => {
    // create i * 1MB images
    const canvases = []

    for (let i = 0; i < nbImage; i++) {
        canvases.push(createImage())
    }

    console.log(`done for ${canvases.length} MB`)
    return canvases
}

const deleteCanvases = canvases => {
    canvases.forEach((canvas, i, a) => {
        canvas.height = 0
        canvas.width = 0
    })
}

let canvases = []
const process = (frequency, size) => {
    setInterval(() => {
        try {
            canvases.push(...createImages(size))
            counter += size
            console.log(`total ${counter}`)
        }
        catch (e) {
            deleteCanvases(canvases)
            canvases = []
        }
    }, frequency)
}


process(2000, 1000)
<a href="02.html">02</a>
<canvas id="test" width="10000" height="1000"></canvas>
<script>
var canvas = document.getElementById("test");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#0000ff";
ctx.fillRect(0,0,10000,1000);
</script>
<a href="01.html">01</a>
<canvas id="test" width="10000" height="1000"></canvas>
<script>
var canvas = document.getElementById("test");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0,0,10000,1000);
</script>
DoResize();

if (typeof canvas === "object" && canvas !== null) {
    canvas.width = 0;
    canvas.height = 0;

    canvas.remove();
    delete canvas;
    canvas = null;
}

canvas = document.createElement("canvas");              
container.appendChild(canvas);

// Just in case, wait for the Browser
window.requestAnimationFrame(() => {
    let context = canvas.getContext("2d");
    context.moveTo(10, 10);
    context.lineTo(30, 30);
    context.stroke();
});