canvas.getContext在画布上调用时不是函数

时间:2017-02-12 23:01:27

标签: javascript html5 canvas

我遇到一个错误,即使元素是画布,也无法调用getContext。

var canvas = document.getElementById(id);
console.log(canvas, canvas.nodeName);
var ctx = canvas.getContext('2d');

谢谢!

它的片段独立工作,但不在脚本



var canvas = document.getElementById( '0_0' );
document.write(canvas.getContext('2d'));

<g class="node" transform="translate(210,330)">
  <canvas x="-8" y="-8" id="0_0"></canvas>
</g>
&#13;
&#13;
&#13;

<canvas x=​"-8" y=​"-8" id=​"0_0">​ "canvas"
Uncaught TypeError: canvas.getContext is not a function
  at Array.populateNodes (script.js:95)
  at Array.Co.call (d3.v3.min.js:3)
  at gen_graph (script.js:63)
  at Object.success (main.js:16)
  at i (jquery.min.js:2)
  at Object.fireWith [as resolveWith] (jquery.min.js:2)
  at A (jquery.min.js:4)
  at XMLHttpRequest.<anonymous> (jquery.min.js:4)

js,使用d3:

道歉我的JS可能有多糟糕,我很陌生

3 个答案:

答案 0 :(得分:4)

我认为“canvas”元素被d3视为SVG的未知“画布”元素。所以“canvas”元素没有映射到HTMLCanvasElement而是映射到文档的domtree中的SVGUnknownElement,因此SVGUnknownElement的getContext是未定义的。

要解决这个问题,你应该用foreignObject elemente包装“canvas”元素,并将xhtml命名空间添加到“canvas”元素。

我不擅长d3,请尝试使用d3构建此结构。

<g class="node" transform="translate(210,330)">
  <foreignObject x="-8" y="-8">
    <canvas id="0_0" xmlns="http://www.w3.org/1999/xhtml"></canvas>
  </foreignObject>
</g>

或者使用image元素而不是“canvas”元素来放置由(html)canvas元素创建的图像。

SVG结构

<g class="node" transform="translate(210,330)">
  <image x="-8" y="-8" id="0_0"/>
</g>

Javascript代码

//create canvas element.
//var canvas = document.getElementById(nodes[i].__data__.name);
var canvas = document.createElement("canvas");
//console.log(canvas, canvas.nodeName);
var ctx = canvas.getContext('2d');

canvas.width = width;
canvas.height = height;

var idata = ctx.createImageData(width, height);
idata.data.set(buffer);
ctx.putImageData(idata, 0, 0);

//set image created by canvas to image element.
var image = document.getElementById(nodes[i].__data__.name);
image.width.baseVal.value = width;
image.height.baseVal.value = height;
image.href.baseVal = canvas.toDataURL();

答案 1 :(得分:0)

我实际上使用d3 v5可以正常工作。我正在打字稿中进行此操作,因此进行了一些估算。

一个问题是,如果仅按常规d3方式附加元素(selection.append()),则会得到更通用类型的选择。您不能仅通过投射来解决此问题。您要做的就是使用document.createElement()在DOM中创建它,然后调用appendChild()放置它。当您这样做时,您将得到一个类型为HTMLCanvasElement的画布。

我没有意识到的一个技巧是,您必须同时在foreignObject和canvas中指定尺寸,否则它将被裁剪。

我在有棱角的土地上,所以看起来像这样

import * as d3 from 'd3';
   ...
private svg: d3.Selection<Element, any, any, any>
private canvasNode: HTMLCanvasElement

constructor(private root: ElementRef) {}

let foreigner = this.svg.append("foreignObject")
foreigner.attr("width", "800")
foreigner.attr("height", "600")

let canvas = document.createElement("canvas")
canvas.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml')
canvas.textContent = 'Drawing canvas is not supported'

this.canvasNode = foreigner.node().appendChild(canvas)
this.canvasNode.setAttribute("width", "800")
this.canvasNode.setAttribute("height", "600")

我的组件实现了AfterViewInit,因此我可以延迟实际绘制(尽管您不必这样做):

ngAfterViewInit(): void {
  let ctx: CanvasRenderingContext2D = this.canvasNode.getContext('2d')
  this.draw(ctx)
}

最后,您可以借鉴一些东西:

draw(ctx: CanvasRenderingContext2D): void {
  ctx.save()

  ctx.strokeStyle = "#FF0000"
  ctx.fillStyle = "#00FFFF"

  ctx.moveTo(0,0)
  ctx.lineTo(2000, 1000);
  ctx.stroke();

  ctx.restore()

  console.log("drawn")
}

我一直在使用angular和d3(在打字稿中)。有时候正确设置所有类型并不容易,但是值得付出努力。

希望这对某人有帮助。

答案 2 :(得分:0)

使用D3.js,至关重要的是使用.create('xhtml:canvas').append('xhtml:canvas')在适当的命名空间中创建canvas元素。 xmlns属性很不错,但是大多数现代HTML5浏览器都会忽略它。

这是完整的D3.js示例:

const svg = d3.select('body').append('svg')
  .style('width', '100%')
  .style('height', '100%');

const group = svg.append('g')
  .attr('class', 'node')
  .attr('transform', 'translate(10,10)');

const foreignObject = group.append('foreignObject')
  .attr('width', 100)
  .attr('height', 100);

const canvas = foreignObject.append('xhtml:canvas')
  .attr('xmlns', 'http://www.w3.org/1999/xhtml');

const context = canvas.node().getContext('2d');

console.log(canvas.node().constructor.name);
// => HTMLCanvasElement

// Draw something
context.fillStyle = 'blue';
context.fillRect(0, 0, 100, 100);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

名称空间的概念不容易理解,并且大多数时候您不需要显式设置它们,因为它们是从父元素继承的。 Mike Bostock在this Github issue中很好地解释了这个概念。

(是的,我打开了问题,它也不知道如何使用名称空间。)