You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Server应用中使用Canvas并在组件加载时触发初始化函数问题问询

问题原因

你遇到的报错是因为Blazor组件是动态渲染的,你把获取canvas节点的代码写在JS全局作用域直接执行时,Blazor组件还没完成渲染,id="canvas"的DOM节点还不存在,document.getElementById("canvas")返回null,调用getContext自然报错。纯前端静态页面的脚本在body末尾/onload阶段执行时DOM已经加载完成,所以没有这个问题。


解决步骤

1. 调整JS脚本逻辑

把全局变量赋值、初始化逻辑都封装到自定义函数内,不要在全局作用域直接执行DOM操作:

// 先声明全局变量,不要直接赋值
let canvas;
let context;
let circleData = [];
// 你的circleList数据源保留原有定义即可

// 把原有onload逻辑封装为TestMe函数
window.TestMe = function() {
    // 函数调用时再获取DOM节点,此时Blazor组件已经渲染完成
    canvas = document.getElementById("canvas");
    context = canvas.getContext('2d');

    var initX = canvas.width / 2;
    var initY = 0;
    circleList.forEach(function (element) {
        initY = initY + parseInt(element.score) + 10;
        var circleObject = {
            centerX: initX,
            centerY: initY,
            radius: parseInt(element.score),
            draging: false,
            overlapMark: false,
            subject: element.subject,
        }
        initY = initY + parseInt(element.score) + 20;
        circleData.push(circleObject);
    });
    drawAll();
}
// 原有drawAll、拖拽相关的函数保留不变

2. 调整Blazor组件逻辑

在组件渲染完成的生命周期钩子中调用JS初始化函数,仅第一次渲染完成后执行一次即可:

@page "/MyPage"
@inject IJSRuntime JSRuntime;
@implements IAsyncDisposable

<button class="btn btn-primary" @onclick="CallJsMethod">Call Js Method</button>

<div class="text-center py-5">
    <canvas id="canvas" class="border" width="500px" ondrop="drop(event, element)" ondragover="allowDrop(event)"></canvas>
</div>

@code {
    // 标记是否已完成初始化,避免重复调用
    private bool _isInitialized;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && !_isInitialized)
        {
            // 组件第一次渲染完成,canvas已挂载到DOM,调用JS初始化
            await JSRuntime.InvokeVoidAsync("TestMe");
            _isInitialized = true;
        }
        await base.OnAfterRenderAsync(firstRender);
    }

    private async Task CallJsMethod()
    {
        // 此处填写你需要调用的其他JS方法即可
        // await JSRuntime.InvokeVoidAsync("你的其他JS方法名");
    }

    // 组件销毁时可选清理Canvas相关资源,避免内存泄漏
    public async ValueTask DisposeAsync()
    {
        // 如果有清理逻辑,此处调用对应的JS方法即可
        // await JSRuntime.InvokeVoidAsync("你的清理逻辑函数名");
    }
}

优化建议

如果组件会被多次加载卸载,推荐用ElementReference传递canvas元素,避免固定id冲突,用法如下:

  1. 给canvas标签加ref属性:<canvas @ref="CanvasRef" ...></canvas>
  2. 组件代码中定义变量:private ElementReference CanvasRef;
  3. 调用JS时传递参数:await JSRuntime.InvokeVoidAsync("TestMe", CanvasRef);
  4. JS函数直接接收元素使用:window.TestMe = function(canvas) { context = canvas.getContext('2d'); ... }

内容的提问来源于stack exchange,提问作者Baba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.20 08:36:07