Что делает CanvasRenderingContext2D CanvasRenderingContext2D? - PullRequest
0 голосов
/ 13 сентября 2018

Рассмотрим следующую веб-страницу.

<html>
    <body>
        <canvas id="canvas" width="300" height="300" style="border:1px solid #000000;">
        </canvas>
    </body>
</html>

Я открываю эту страницу в Firefox, открываю консоль JS и набираю следующее.

> document.getElementById("canvas").getContext("2d")

Вывод выглядит следующим образом:

CanvasRenderingContext2D { canvas: canvas#canvas, mozCurrentTransform: (6) […], mozCurrentTransformInverse: (6) […], mozTextStyle: "10px sans-serif", mozImageSmoothingEnabled: true, globalAlpha: 1, globalCompositeOperation: "source-over", strokeStyle: "#000000", fillStyle: "#000000", filter: "none" }

С другой стороны, если я сам создаю объект и копирую все кишки CanvasRenderingContext2D, это все равно просто обычный объект.

var realContext = document.getElementById("canvas").getContext("2d")
var myContext = new Object()
for (var property in realContext) {
    myContext[property] = realContext[property];
}

myContext
Object { drawImage: drawImage(), beginPath: beginPath(), fill: fill(), stroke: stroke(), clip: clip(), isPointInPath: isPointInPath(), isPointInStroke: isPointInStroke(), createLinearGradient: createLinearGradient(), createRadialGradient: createRadialGradient(), createPattern: createPattern(), … }

Что делает CanvasRenderingContext2D CanvasRenderingContext2D?

Как следствие, как я могу превратить мой простой старый объект в a CanvasRenderingContext2D?


Редактировать : Мне все равно, что говорит консоль JS. Меня волнует, что я не могу использовать свой новый контекст так же, как я использую оригинальный.

myContext.save()
TypeError: 'save' called on an object that does not implement interface CanvasRenderingContext2D

Цель состоит в том, чтобы иметь возможность использовать новый объект точно так же, как старый, и рисовать на оригинальном холсте.

Редактировать: Я искал решения, которые не требуют модификации исходного кода сайта с использованием canvas.

1 Ответ

0 голосов
/ 13 сентября 2018

Вот фрагмент, который начнет регистрировать все обращения к любому CanvasRenderingContext2D.Я отключил фрагмент стека, потому что он выдает кучу ошибок, пытаясь сериализовать вывод console.log(), поэтому просто проверьте на реальной консоли фактический вывод.

// handler used for intercepting proxy
const handler = {
  get (target, key) {
    // forward access to underlying property
    const value = Reflect.get(target, key)
    console.log('get', target, key, value)
    // return underlying value as an intercepting proxy
    // if value is function, its calling context will be the target
    return proxyValue(target, value)
  },
  set (target, key, value) {
    // forward access to underlying property
    Reflect.set(target, key, value)
    console.log('set', target, key, value)
    // return set value as an intercepting proxy
    // if value is function, it will not have a calling context
    return proxyValue(undefined, value)
  },
  apply (target, thisArg, args) {
    // forward invocation to underlying function
    const value = Reflect.apply(target, thisArg, args)
    console.log('apply', thisArg, target, args, value)
    // return underlying return value as an intercepting proxy
    // if value is a function, it will not have a calling context
    return proxyValue(undefined, value)
  }
}

// wrap all accessed non-primitives in an intercepting proxy
function proxyValue (target, value) {
  switch (typeof value) {
  case 'function':
    // return function bound to target as proxy
    return new Proxy(value.bind(target), handler)
  case 'object':
    // return object as proxy
    return new Proxy(value, handler)
  default:
    // return primitive as normal
    return value
  }
}

// iterate all descriptors of CanvasRenderingContext2D prototype
const descriptors = Object.getOwnPropertyDescriptors(CanvasRenderingContext2D.prototype)

Object.entries(descriptors).forEach(([key, { value, get, set, configurable, enumerable, writable }]) => {
  // if this is an accessor property
  const accessor = get || set ? {
    get () {
      // forward access to underlying getter
      const getValue = Reflect.apply(get, this, [])
      console.log('get', this, key, getValue)
      // return underlying value as an intercepting proxy
      return proxyValue(this, getValue)
    },
    set (setValue) {
      // forward access to underlying setter
      Reflect.apply(set, this, [setValue])
      console.log('set', this, key, setValue)
      // return set value as an intercepting proxy
      return proxyValue(this, setValue)
    },
    configurable,
    enumerable
  } : {
    get () {
      console.log('get', this, key, value)
      // return underlying value as an intercepting proxy
      return proxyValue(this, value)
    },
    set (setValue) {
      // assign and return underlying value
      value = setValue
      console.log('set', this, key, setValue)
      // return set value as an intercepting proxy
      return proxyValue(this, setValue)
    },
    configurable,
    enumerable
  }

  // overwrite property with intercepting accessor property
  Object.defineProperty(CanvasRenderingContext2D.prototype, key, accessor)
})

const canvas = document.querySelector('canvas')
const context = canvas.getContext('2d')

context.fillStyle = 'red'
context.save()
context.getImageData(0, 0, 100, 100).data
<canvas></canvas>

Список литературы

...