Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supporting existing canvas element for createCanvas() and createGraphics() #6229

Merged
merged 5 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions src/core/p5.Graphics.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,26 @@ import * as constants from './constants';
* @param {Number} h height
* @param {Constant} renderer the renderer to use, either P2D or WEBGL
* @param {p5} [pInst] pointer to p5 instance
* @param {Object} [canvas] existing html canvas element
*/
p5.Graphics = class extends p5.Element {
constructor(w, h, renderer, pInst) {
let canvas = document.createElement('canvas');
super(canvas, pInst);
constructor(w, h, renderer, pInst, canvas) {
let canvasTemp;
if (canvas) {
canvasTemp = canvas;
} else {
canvasTemp = document.createElement('canvas');
}

super(canvasTemp, pInst);
this.canvas = canvasTemp;

const r = renderer || constants.P2D;

this.canvas = canvas;
const node = pInst._userNode || document.body;
node.appendChild(this.canvas);
if (!canvas) {
node.appendChild(this.canvas);
}

// bind methods and props of p5 to the new object
for (const p in p5.prototype) {
Expand Down
117 changes: 82 additions & 35 deletions src/core/rendering.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ const defaultClass = 'p5Canvas';
* function. If <a href="#/p5/createCanvas">createCanvas()</a> is not used, the
* window will be given a default size of 100×100 pixels.
*
* Optionally, an existing canvas can be passed using a selector, ie. `document.getElementById('')`.
* If specified, avoid using `setAttributes()` afterwards, as this will remove and recreate the existing canvas.
*
* For more ways to position the canvas, see the
* <a href='https://github.com/processing/p5.js/wiki/Positioning-your-canvas'>
* positioning the canvas</a> wiki page.
Expand All @@ -42,6 +45,7 @@ const defaultClass = 'p5Canvas';
* @param {Number} w width of the canvas
* @param {Number} h height of the canvas
* @param {Constant} [renderer] either P2D or WEBGL
* @param {Object} [canvas] existing html canvas element
* @return {p5.Renderer} pointer to p5.Renderer holding canvas
* @example
* <div>
Expand All @@ -57,56 +61,84 @@ const defaultClass = 'p5Canvas';
* @alt
* Black line extending from top-left of canvas to bottom right.
*/
p5.prototype.createCanvas = function(w, h, renderer) {
/**
* @method createCanvas
* @param {Number} w
* @param {Number} h
* @param {Object} [canvas]
* @return {p5.Renderer} pointer to p5.Renderer holding canvas
*/
p5.prototype.createCanvas = function(w, h, renderer, canvas) {
p5._validateParameters('createCanvas', arguments);
//optional: renderer, otherwise defaults to p2d
const r = renderer || constants.P2D;

let r;
if (arguments[2] instanceof HTMLCanvasElement) {
renderer = constants.P2D;
canvas = arguments[2];
} else {
r = renderer || constants.P2D;
}

let c;

if (r === constants.WEBGL) {
if (canvas) {
c = document.getElementById(defaultId);
if (c) {
//if defaultCanvas already exists
c.parentNode.removeChild(c); //replace the existing defaultCanvas
const thisRenderer = this._renderer;
this._elements = this._elements.filter(e => e !== thisRenderer);
}
c = document.createElement('canvas');
c.id = defaultId;
c.classList.add(defaultClass);
c = canvas;
this._defaultGraphicsCreated = false;
} else {
if (!this._defaultGraphicsCreated) {
c = document.createElement('canvas');
let i = 0;
while (document.getElementById(`defaultCanvas${i}`)) {
i++;
if (r === constants.WEBGL) {
c = document.getElementById(defaultId);
if (c) {
//if defaultCanvas already exists
c.parentNode.removeChild(c); //replace the existing defaultCanvas
const thisRenderer = this._renderer;
this._elements = this._elements.filter(e => e !== thisRenderer);
}
defaultId = `defaultCanvas${i}`;
c = document.createElement('canvas');
c.id = defaultId;
c.classList.add(defaultClass);
} else {
// resize the default canvas if new one is created
c = this.canvas;
if (!this._defaultGraphicsCreated) {
if (canvas) {
c = canvas;
} else {
c = document.createElement('canvas');
}
let i = 0;
while (document.getElementById(`defaultCanvas${i}`)) {
i++;
}
defaultId = `defaultCanvas${i}`;
c.id = defaultId;
c.classList.add(defaultClass);
} else {
// resize the default canvas if new one is created
c = this.canvas;
}
}
}

// set to invisible if still in setup (to prevent flashing with manipulate)
if (!this._setupDone) {
c.dataset.hidden = true; // tag to show later
c.style.visibility = 'hidden';
}
// set to invisible if still in setup (to prevent flashing with manipulate)
if (!this._setupDone) {
c.dataset.hidden = true; // tag to show later
c.style.visibility = 'hidden';
}

if (this._userNode) {
// user input node case
this._userNode.appendChild(c);
} else {
//create main element
if (document.getElementsByTagName('main').length === 0) {
let m = document.createElement('main');
document.body.appendChild(m);
if (this._userNode) {
// user input node case
this._userNode.appendChild(c);
} else {
//create main element
if (document.getElementsByTagName('main').length === 0) {
let m = document.createElement('main');
document.body.appendChild(m);
}
//append canvas to main
document.getElementsByTagName('main')[0].appendChild(c);
}
//append canvas to main
document.getElementsByTagName('main')[0].appendChild(c);
}

// Init our graphics renderer
Expand Down Expand Up @@ -216,11 +248,15 @@ p5.prototype.noCanvas = function() {
* to check what version is being used, or call <a href="#/p5/setAttributes">pg.setAttributes({ version: 1 })</a>
* to create a WebGL1 context.
*
* Optionally, an existing canvas can be passed using a selector, ie. document.getElementById('').
* By default this canvas will be hidden (offscreen buffer), to make visible, set element's style to display:block;
*
* @method createGraphics
* @param {Number} w width of the offscreen graphics buffer
* @param {Number} h height of the offscreen graphics buffer
* @param {Constant} [renderer] either P2D or WEBGL
* undefined defaults to p2d
* @param {Object} [canvas] existing html canvas element
* @return {p5.Graphics} offscreen graphics buffer
* @example
* <div>
Expand All @@ -245,9 +281,20 @@ p5.prototype.noCanvas = function() {
* @alt
* 4 grey squares alternating light and dark grey. White quarter circle mid-left.
*/
p5.prototype.createGraphics = function(w, h, renderer) {
/**
* @method createGraphics
* @param {Number} w
* @param {Number} h
* @param {Object} [canvas]
* @return {p5.Graphics} offscreen graphics buffer
*/
p5.prototype.createGraphics = function(w, h, renderer, canvas) {
if (arguments[2] instanceof HTMLCanvasElement) {
renderer = constants.P2D;
canvas = arguments[2];
}
p5._validateParameters('createGraphics', arguments);
return new p5.Graphics(w, h, renderer, this);
return new p5.Graphics(w, h, renderer, this, canvas);
};

/**
Expand Down