Creates a new <video> element that contains the audio/video feed
911 | * from a webcam. This can be drawn onto the canvas using video().
912 | * More specific properties of the feed can be passing in a Constraints object.
913 | * See the
914 | * W3C
915 | * spec for possible properties. Note that not all of these are supported
916 | * by all browsers.
917 | * Security note: A new browser security specification requires that getUserMedia,
918 | * which is behind createCapture(), only works when you're running the code locally,
919 | * or on HTTPS. Learn more here
920 | * and here.
921 | *
922 | * @method createCapture
923 | * @param {String|Constant|Object} type type of capture, either VIDEO or
924 | * AUDIO if none specified, default both,
925 | * or a Constraints object
926 | * @param {Function} callback function to be called once
927 | * stream has loaded
928 | * @return {Object/p5.Element} capture video p5.Element
929 | * @example
930 | *
1020 | * var h2 = createElement('h2','im an h2 p5.element!');
1021 | *
1022 | */
1023 | p5.prototype.createElement = function(tag, content) {
1024 | var elt = document.createElement(tag);
1025 | if (typeof content !== 'undefined') {
1026 | elt.innerHTML = content;
1027 | }
1028 | return addElement(elt, this);
1029 | };
1030 |
1031 |
1032 | // =============================================================================
1033 | // p5.Element additions
1034 | // =============================================================================
1035 | /**
1036 | *
1037 | * Adds specified class to the element.
1038 | *
1039 | * @for p5.Element
1040 | * @method addClass
1041 | * @param {String} class name of class to add
1042 | * @return {Object/p5.Element}
1043 | * @example
1044 | *
1045 | * var div = createDiv('div');
1046 | * div.addClass('myClass');
1047 | *
1048 | */
1049 | p5.Element.prototype.addClass = function(c) {
1050 | if (this.elt.className) {
1051 | // PEND don't add class more than once
1052 | //var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?');
1053 | //if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) {
1054 | this.elt.className = this.elt.className+' '+c;
1055 | //}
1056 | } else {
1057 | this.elt.className = c;
1058 | }
1059 | return this;
1060 | }
1061 |
1062 | /**
1063 | *
1064 | * Removes specified class from the element.
1065 | *
1066 | * @method removeClass
1067 | * @param {String} class name of class to remove
1068 | * @return {Object/p5.Element}
1069 | */
1070 | p5.Element.prototype.removeClass = function(c) {
1071 | var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)');
1072 | this.elt.className = this.elt.className.replace(regex, '');
1073 | this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional)
1074 | return this;
1075 | }
1076 |
1077 | /**
1078 | *
1079 | * Attaches the element as a child to the parent specified.
1080 | * Accepts either a string ID, DOM node, or p5.Element.
1081 | * If no argument is specified, an array of children DOM nodes is returned.
1082 | *
1083 | * @method child
1084 | * @param {String|Object|p5.Element} [child] the ID, DOM node, or p5.Element
1085 | * to add to the current element
1086 | * @return {p5.Element}
1087 | * @example
1088 | *
1089 | * var div0 = createDiv('this is the parent');
1090 | * var div1 = createDiv('this is the child');
1091 | * div0.child(div1); // use p5.Element
1092 | *
1093 | *
1094 | * var div0 = createDiv('this is the parent');
1095 | * var div1 = createDiv('this is the child');
1096 | * div1.id('apples');
1097 | * div0.child('apples'); // use id
1098 | *
1099 | *
1100 | * var div0 = createDiv('this is the parent');
1101 | * var elt = document.getElementById('myChildDiv');
1102 | * div0.child(elt); // use element from page
1103 | *
1104 | */
1105 | p5.Element.prototype.child = function(c) {
1106 | if (c === null){
1107 | return this.elt.childNodes
1108 | }
1109 | if (typeof c === 'string') {
1110 | if (c[0] === '#') {
1111 | c = c.substring(1);
1112 | }
1113 | c = document.getElementById(c);
1114 | } else if (c instanceof p5.Element) {
1115 | c = c.elt;
1116 | }
1117 | this.elt.appendChild(c);
1118 | return this;
1119 | };
1120 |
1121 | /**
1122 | * Centers a p5 Element either vertically, horizontally,
1123 | * or both, relative to its parent or according to
1124 | * the body if the Element has no parent. If no argument is passed
1125 | * the Element is aligned both vertically and horizontally.
1126 | *
1127 | * @param {String} align passing 'vertical', 'horizontal' aligns element accordingly
1128 | * @return {Object/p5.Element} pointer to p5.Element
1129 | * @example
1130 | *
1131 | * function setup() {
1132 | * var div = createDiv('').size(10,10);
1133 | * div.style('background-color','orange');
1134 | * div.center();
1135 | *
1136 | * }
1137 | *
1138 | */
1139 | p5.Element.prototype.center = function(align) {
1140 | var style = this.elt.style.display;
1141 | var hidden = this.elt.style.display === 'none';
1142 | var parentHidden = this.parent().style.display === 'none';
1143 | var pos = { x : this.elt.offsetLeft, y : this.elt.offsetTop };
1144 |
1145 | if (hidden) this.show();
1146 |
1147 | this.elt.style.display = 'block';
1148 | this.position(0,0);
1149 |
1150 | if (parentHidden) this.parent().style.display = 'block';
1151 |
1152 | var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth);
1153 | var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight);
1154 | var y = pos.y;
1155 | var x = pos.x;
1156 |
1157 | if (align === 'both' || align === undefined){
1158 | this.position(wOffset/2, hOffset/2);
1159 | }else if (align === 'horizontal'){
1160 | this.position(wOffset/2, y);
1161 | }else if (align === 'vertical'){
1162 | this.position(x, hOffset/2);
1163 | }
1164 |
1165 | this.style('display', style);
1166 |
1167 | if (hidden) this.hide();
1168 |
1169 | if (parentHidden) this.parent().style.display = 'none';
1170 |
1171 | return this;
1172 | };
1173 |
1174 | /**
1175 | *
1176 | * If an argument is given, sets the inner HTML of the element,
1177 | * replacing any existing html. If no arguments are given, returns
1178 | * the inner HTML of the element.
1179 | *
1180 | * @for p5.Element
1181 | * @method html
1182 | * @param {String} [html] the HTML to be placed inside the element
1183 | * @return {Object/p5.Element|String}
1184 | * @example
1185 | *
1186 | * var div = createDiv('').size(100,100);
1187 | * div.style('background-color','orange');
1188 | * div.html('hi');
1189 | *
1190 | */
1191 | p5.Element.prototype.html = function(html) {
1192 | if (typeof html !== 'undefined') {
1193 | this.elt.innerHTML = html;
1194 | return this;
1195 | } else {
1196 | return this.elt.innerHTML;
1197 | }
1198 | };
1199 |
1200 | /**
1201 | *
1202 | * Sets the position of the element relative to (0, 0) of the
1203 | * window. Essentially, sets position:absolute and left and top
1204 | * properties of style. If no arguments given returns the x and y position
1205 | * of the element in an object.
1206 | *
1207 | * @method position
1208 | * @param {Number} [x] x-position relative to upper left of window
1209 | * @param {Number} [y] y-position relative to upper left of window
1210 | * @return {Object/p5.Element}
1211 | * @example
1212 | *
1213 | * function setup() {
1214 | * var cnv = createCanvas(100, 100);
1215 | * // positions canvas 50px to the right and 100px
1216 | * // below upper left corner of the window
1217 | * cnv.position(50, 100);
1218 | * }
1219 | *
1220 | */
1221 | p5.Element.prototype.position = function() {
1222 | if (arguments.length === 0){
1223 | return { 'x' : this.elt.offsetLeft , 'y' : this.elt.offsetTop };
1224 | }else{
1225 | this.elt.style.position = 'absolute';
1226 | this.elt.style.left = arguments[0]+'px';
1227 | this.elt.style.top = arguments[1]+'px';
1228 | this.x = arguments[0];
1229 | this.y = arguments[1];
1230 | return this;
1231 | }
1232 | };
1233 |
1234 | /* Helper method called by p5.Element.style() */
1235 | p5.Element.prototype._translate = function(){
1236 | this.elt.style.position = 'absolute';
1237 | // save out initial non-translate transform styling
1238 | var transform = '';
1239 | if (this.elt.style.transform) {
1240 | transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
1241 | transform = transform.replace(/translate[X-Z]?\(.*\)/g, '');
1242 | }
1243 | if (arguments.length === 2) {
1244 | this.elt.style.transform = 'translate('+arguments[0]+'px, '+arguments[1]+'px)';
1245 | } else if (arguments.length > 2) {
1246 | this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)';
1247 | if (arguments.length === 3) {
1248 | this.elt.parentElement.style.perspective = '1000px';
1249 | } else {
1250 | this.elt.parentElement.style.perspective = arguments[3]+'px';
1251 | }
1252 | }
1253 | // add any extra transform styling back on end
1254 | this.elt.style.transform += transform;
1255 | return this;
1256 | };
1257 |
1258 | /* Helper method called by p5.Element.style() */
1259 | p5.Element.prototype._rotate = function(){
1260 | // save out initial non-rotate transform styling
1261 | var transform = '';
1262 | if (this.elt.style.transform) {
1263 | var transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
1264 | transform = transform.replace(/rotate[X-Z]?\(.*\)/g, '');
1265 | }
1266 |
1267 | if (arguments.length === 1){
1268 | this.elt.style.transform = 'rotate('+arguments[0]+'deg)';
1269 | }else if (arguments.length === 2){
1270 | this.elt.style.transform = 'rotate('+arguments[0]+'deg, '+arguments[1]+'deg)';
1271 | }else if (arguments.length === 3){
1272 | this.elt.style.transform = 'rotateX('+arguments[0]+'deg)';
1273 | this.elt.style.transform += 'rotateY('+arguments[1]+'deg)';
1274 | this.elt.style.transform += 'rotateZ('+arguments[2]+'deg)';
1275 | }
1276 | // add remaining transform back on
1277 | this.elt.style.transform += transform;
1278 | return this;
1279 | };
1280 |
1281 | /**
1282 | * Sets the given style (css) property (1st arg) of the element with the
1283 | * given value (2nd arg). If a single argument is given, .style()
1284 | * returns the value of the given property; however, if the single argument
1285 | * is given in css syntax ('text-align:center'), .style() sets the css
1286 | * appropriatly. .style() also handles 2d and 3d css transforms. If
1287 | * the 1st arg is 'rotate', 'translate', or 'position', the following arguments
1288 | * accept Numbers as values. ('translate', 10, 100, 50);
1289 | *
1290 | * @method style
1291 | * @param {String} property property to be set
1292 | * @param {String|Number|p5.Color} [value] value to assign to property
1293 | * @param {String|Number} [value] value to assign to property (rotate/translate)
1294 | * @param {String|Number} [value] value to assign to property (rotate/translate)
1295 | * @param {String|Number} [value] value to assign to property (translate)
1296 | * @return {String|Object/p5.Element} value of property, if no value is specified
1297 | * or p5.Element
1298 | * @example
1299 | *
1300 | * var myDiv = createDiv("I like pandas.");
1301 | * myDiv.style("font-size", "18px");
1302 | * myDiv.style("color", "#ff0000");
1303 | *
1304 | *
1305 | * var col = color(25,23,200,50);
1306 | * var button = createButton("button");
1307 | * button.style("background-color", col);
1308 | * button.position(10, 10);
1309 | *
1310 | *
1311 | * var myDiv = createDiv("I like lizards.");
1312 | * myDiv.style("position", 20, 20);
1313 | * myDiv.style("rotate", 45);
1314 | *
1315 | *
1316 | * var myDiv;
1317 | * function setup() {
1318 | * background(200);
1319 | * myDiv = createDiv("I like gray.");
1320 | * myDiv.position(20, 20);
1321 | * }
1322 | *
1323 | * function draw() {
1324 | * myDiv.style("font-size", mouseX+"px");
1325 | * }
1326 | *
1327 | */
1328 | p5.Element.prototype.style = function(prop, val) {
1329 | var self = this;
1330 |
1331 | if (val instanceof p5.Color) {
1332 | val = 'rgba(' + val.levels[0] + ',' + val.levels[1] + ',' + val.levels[2] + ',' + val.levels[3]/255 + ')'
1333 | }
1334 |
1335 | if (typeof val === 'undefined') {
1336 | if (prop.indexOf(':') === -1) {
1337 | var styles = window.getComputedStyle(self.elt);
1338 | var style = styles.getPropertyValue(prop);
1339 | return style;
1340 | } else {
1341 | var attrs = prop.split(';');
1342 | for (var i = 0; i < attrs.length; i++) {
1343 | var parts = attrs[i].split(':');
1344 | if (parts[0] && parts[1]) {
1345 | this.elt.style[parts[0].trim()] = parts[1].trim();
1346 | }
1347 | }
1348 | }
1349 | } else {
1350 | if (prop === 'rotate' || prop === 'translate' || prop === 'position'){
1351 | var trans = Array.prototype.shift.apply(arguments);
1352 | var f = this[trans] || this['_'+trans];
1353 | f.apply(this, arguments);
1354 | } else {
1355 | this.elt.style[prop] = val;
1356 | if (prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top') {
1357 | var numVal = val.replace(/\D+/g, '');
1358 | this[prop] = parseInt(numVal, 10); // pend: is this necessary?
1359 | }
1360 | }
1361 | }
1362 | return this;
1363 | };
1364 |
1365 |
1366 | /**
1367 | *
1368 | * Adds a new attribute or changes the value of an existing attribute
1369 | * on the specified element. If no value is specified, returns the
1370 | * value of the given attribute, or null if attribute is not set.
1371 | *
1372 | * @method attribute
1373 | * @param {String} attr attribute to set
1374 | * @param {String} [value] value to assign to attribute
1375 | * @return {String|Object/p5.Element} value of attribute, if no value is
1376 | * specified or p5.Element
1377 | * @example
1378 | *
1379 | * var myDiv = createDiv("I like pandas.");
1380 | * myDiv.attribute("align", "center");
1381 | *
1382 | */
1383 | p5.Element.prototype.attribute = function(attr, value) {
1384 | if (typeof value === 'undefined') {
1385 | return this.elt.getAttribute(attr);
1386 | } else {
1387 | this.elt.setAttribute(attr, value);
1388 | return this;
1389 | }
1390 | };
1391 |
1392 |
1393 | /**
1394 | *
1395 | * Removes an attribute on the specified element.
1396 | *
1397 | * @method removeAttribute
1398 | * @param {String} attr attribute to remove
1399 | * @return {Object/p5.Element}
1400 | *
1401 | * @example
1402 | *
1403 | * var button;
1404 | * var checkbox;
1405 | *
1406 | * function setup() {
1407 | * checkbox = createCheckbox('enable', true);
1408 | * checkbox.changed(enableButton);
1409 | * button = createButton('button');
1410 | * button.position(10, 10);
1411 | * }
1412 | *
1413 | * function enableButton() {
1414 | * if( this.checked() ) {
1415 | * // Re-enable the button
1416 | * button.removeAttribute('disabled');
1417 | * } else {
1418 | * // Disable the button
1419 | * button.attribute('disabled','');
1420 | * }
1421 | * }
1422 | *
1423 | */
1424 | p5.Element.prototype.removeAttribute = function(attr) {
1425 | this.elt.removeAttribute(attr);
1426 | return this;
1427 | };
1428 |
1429 |
1430 | /**
1431 | * Either returns the value of the element if no arguments
1432 | * given, or sets the value of the element.
1433 | *
1434 | * @method value
1435 | * @param {String|Number} [value]
1436 | * @return {String|Object/p5.Element} value of element if no value is specified or p5.Element
1437 | * @example
1438 | *
1439 | * // gets the value
1440 | * var inp;
1441 | * function setup() {
1442 | * inp = createInput('');
1443 | * }
1444 | *
1445 | * function mousePressed() {
1446 | * print(inp.value());
1447 | * }
1448 | *
1449 | *
1450 | * // sets the value
1451 | * var inp;
1452 | * function setup() {
1453 | * inp = createInput('myValue');
1454 | * }
1455 | *
1456 | * function mousePressed() {
1457 | * inp.value("myValue");
1458 | * }
1459 | *
1460 | */
1461 | p5.Element.prototype.value = function() {
1462 | if (arguments.length > 0) {
1463 | this.elt.value = arguments[0];
1464 | return this;
1465 | } else {
1466 | if (this.elt.type === 'range') {
1467 | return parseFloat(this.elt.value);
1468 | }
1469 | else return this.elt.value;
1470 | }
1471 | };
1472 |
1473 | /**
1474 | *
1475 | * Shows the current element. Essentially, setting display:block for the style.
1476 | *
1477 | * @method show
1478 | * @return {Object/p5.Element}
1479 | * @example
1480 | *
1481 | * var div = createDiv('div');
1482 | * div.style("display", "none");
1483 | * div.show(); // turns display to block
1484 | *
1485 | */
1486 | p5.Element.prototype.show = function() {
1487 | this.elt.style.display = 'block';
1488 | return this;
1489 | };
1490 |
1491 | /**
1492 | * Hides the current element. Essentially, setting display:none for the style.
1493 | *
1494 | * @method hide
1495 | * @return {Object/p5.Element}
1496 | * @example
1497 | *
1498 | * var div = createDiv('this is a div');
1499 | * div.hide();
1500 | *
1501 | */
1502 | p5.Element.prototype.hide = function() {
1503 | this.elt.style.display = 'none';
1504 | return this;
1505 | };
1506 |
1507 | /**
1508 | *
1509 | * Sets the width and height of the element. AUTO can be used to
1510 | * only adjust one dimension. If no arguments given returns the width and height
1511 | * of the element in an object.
1512 | *
1513 | * @method size
1514 | * @param {Number} [w] width of the element
1515 | * @param {Number} [h] height of the element
1516 | * @return {Object/p5.Element}
1517 | * @example
1518 | *
1519 | * var div = createDiv('this is a div');
1520 | * div.size(100, 100);
1521 | *
1522 | */
1523 | p5.Element.prototype.size = function(w, h) {
1524 | if (arguments.length === 0){
1525 | return { 'width' : this.elt.offsetWidth , 'height' : this.elt.offsetHeight };
1526 | }else{
1527 | var aW = w;
1528 | var aH = h;
1529 | var AUTO = p5.prototype.AUTO;
1530 | if (aW !== AUTO || aH !== AUTO) {
1531 | if (aW === AUTO) {
1532 | aW = h * this.width / this.height;
1533 | } else if (aH === AUTO) {
1534 | aH = w * this.height / this.width;
1535 | }
1536 | // set diff for cnv vs normal div
1537 | if (this.elt instanceof HTMLCanvasElement) {
1538 | var j = {};
1539 | var k = this.elt.getContext('2d');
1540 | for (var prop in k) {
1541 | j[prop] = k[prop];
1542 | }
1543 | this.elt.setAttribute('width', aW * this._pInst._pixelDensity);
1544 | this.elt.setAttribute('height', aH * this._pInst._pixelDensity);
1545 | this.elt.setAttribute('style', 'width:' + aW + 'px; height:' + aH + 'px');
1546 | this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity);
1547 | for (var prop in j) {
1548 | this.elt.getContext('2d')[prop] = j[prop];
1549 | }
1550 | } else {
1551 | this.elt.style.width = aW+'px';
1552 | this.elt.style.height = aH+'px';
1553 | this.elt.width = aW;
1554 | this.elt.height = aH;
1555 | this.width = aW;
1556 | this.height = aH;
1557 | }
1558 |
1559 | this.width = this.elt.offsetWidth;
1560 | this.height = this.elt.offsetHeight;
1561 |
1562 | if (this._pInst) { // main canvas associated with p5 instance
1563 | if (this._pInst._curElement.elt === this.elt) {
1564 | this._pInst._setProperty('width', this.elt.offsetWidth);
1565 | this._pInst._setProperty('height', this.elt.offsetHeight);
1566 | }
1567 | }
1568 | }
1569 | return this;
1570 | }
1571 | };
1572 |
1573 | /**
1574 | * Removes the element and deregisters all listeners.
1575 | * @method remove
1576 | * @example
1577 | *
1578 | * var myDiv = createDiv('this is some text');
1579 | * myDiv.remove();
1580 | *
1581 | */
1582 | p5.Element.prototype.remove = function() {
1583 | // deregister events
1584 | for (var ev in this._events) {
1585 | this.elt.removeEventListener(ev, this._events[ev]);
1586 | }
1587 | if (this.elt.parentNode) {
1588 | this.elt.parentNode.removeChild(this.elt);
1589 | }
1590 | delete(this);
1591 | };
1592 |
1593 |
1594 |
1595 | // =============================================================================
1596 | // p5.MediaElement additions
1597 | // =============================================================================
1598 |
1599 |
1600 | /**
1601 | * Extends p5.Element to handle audio and video. In addition to the methods
1602 | * of p5.Element, it also contains methods for controlling media. It is not
1603 | * called directly, but p5.MediaElements are created by calling createVideo,
1604 | * createAudio, and createCapture.
1605 | *
1606 | * @class p5.MediaElement
1607 | * @constructor
1608 | * @param {String} elt DOM node that is wrapped
1609 | * @param {Object} [pInst] pointer to p5 instance
1610 | */
1611 | p5.MediaElement = function(elt, pInst) {
1612 | p5.Element.call(this, elt, pInst);
1613 |
1614 | var self = this;
1615 | this.elt.crossOrigin = 'anonymous';
1616 |
1617 | this._prevTime = 0;
1618 | this._cueIDCounter = 0;
1619 | this._cues = [];
1620 | this._pixelDensity = 1;
1621 |
1622 | /**
1623 | * Path to the media element source.
1624 | *
1625 | * @property src
1626 | * @return {String} src
1627 | */
1628 | Object.defineProperty(self, 'src', {
1629 | get: function() {
1630 | var firstChildSrc = self.elt.children[0].src;
1631 | var srcVal = self.elt.src === window.location.href ? '' : self.elt.src;
1632 | var ret = firstChildSrc === window.location.href ? srcVal : firstChildSrc;
1633 | return ret;
1634 | },
1635 | set: function(newValue) {
1636 | for (var i = 0; i < self.elt.children.length; i++) {
1637 | self.elt.removeChild(self.elt.children[i]);
1638 | }
1639 | var source = document.createElement('source');
1640 | source.src = newValue;
1641 | elt.appendChild(source);
1642 | self.elt.src = newValue;
1643 | },
1644 | });
1645 |
1646 | // private _onended callback, set by the method: onended(callback)
1647 | self._onended = function() {};
1648 | self.elt.onended = function() {
1649 | self._onended(self);
1650 | }
1651 | };
1652 | p5.MediaElement.prototype = Object.create(p5.Element.prototype);
1653 |
1654 |
1655 |
1656 |
1657 | /**
1658 | * Play an HTML5 media element.
1659 | *
1660 | * @method play
1661 | * @return {Object/p5.Element}
1662 | */
1663 | p5.MediaElement.prototype.play = function() {
1664 | if (this.elt.currentTime === this.elt.duration) {
1665 | this.elt.currentTime = 0;
1666 | }
1667 |
1668 | if (this.elt.readyState > 1) {
1669 | this.elt.play();
1670 | } else {
1671 | // in Chrome, playback cannot resume after being stopped and must reload
1672 | this.elt.load();
1673 | this.elt.play();
1674 | }
1675 | return this;
1676 | };
1677 |
1678 | /**
1679 | * Stops an HTML5 media element (sets current time to zero).
1680 | *
1681 | * @method stop
1682 | * @return {Object/p5.Element}
1683 | */
1684 | p5.MediaElement.prototype.stop = function() {
1685 | this.elt.pause();
1686 | this.elt.currentTime = 0;
1687 | return this;
1688 | };
1689 |
1690 | /**
1691 | * Pauses an HTML5 media element.
1692 | *
1693 | * @method pause
1694 | * @return {Object/p5.Element}
1695 | */
1696 | p5.MediaElement.prototype.pause = function() {
1697 | this.elt.pause();
1698 | return this;
1699 | };
1700 |
1701 | /**
1702 | * Set 'loop' to true for an HTML5 media element, and starts playing.
1703 | *
1704 | * @method loop
1705 | * @return {Object/p5.Element}
1706 | */
1707 | p5.MediaElement.prototype.loop = function() {
1708 | this.elt.setAttribute('loop', true);
1709 | this.play();
1710 | return this;
1711 | };
1712 | /**
1713 | * Set 'loop' to false for an HTML5 media element. Element will stop
1714 | * when it reaches the end.
1715 | *
1716 | * @method noLoop
1717 | * @return {Object/p5.Element}
1718 | */
1719 | p5.MediaElement.prototype.noLoop = function() {
1720 | this.elt.setAttribute('loop', false);
1721 | return this;
1722 | };
1723 |
1724 |
1725 | /**
1726 | * Set HTML5 media element to autoplay or not.
1727 | *
1728 | * @method autoplay
1729 | * @param {Boolean} autoplay whether the element should autoplay
1730 | * @return {Object/p5.Element}
1731 | */
1732 | p5.MediaElement.prototype.autoplay = function(val) {
1733 | this.elt.setAttribute('autoplay', val);
1734 | return this;
1735 | };
1736 |
1737 | /**
1738 | * Sets volume for this HTML5 media element. If no argument is given,
1739 | * returns the current volume.
1740 | *
1741 | * @param {Number} [val] volume between 0.0 and 1.0
1742 | * @return {Number|p5.MediaElement} current volume or p5.MediaElement
1743 | * @method volume
1744 | */
1745 | p5.MediaElement.prototype.volume = function(val) {
1746 | if (typeof val === 'undefined') {
1747 | return this.elt.volume;
1748 | } else {
1749 | this.elt.volume = val;
1750 | }
1751 | };
1752 |
1753 | /**
1754 | * If no arguments are given, returns the current playback speed of the
1755 | * element. The speed parameter sets the speed where 2.0 will play the
1756 | * element twice as fast, 0.5 will play at half the speed, and -1 will play
1757 | * the element in normal speed in reverse.(Note that not all browsers support
1758 | * backward playback and even if they do, playback might not be smooth.)
1759 | *
1760 | * @method speed
1761 | * @param {Number} [speed] speed multiplier for element playback
1762 | * @return {Number|Object/p5.MediaElement} current playback speed or p5.MediaElement
1763 | */
1764 | p5.MediaElement.prototype.speed = function(val) {
1765 | if (typeof val === 'undefined') {
1766 | return this.elt.playbackRate;
1767 | } else {
1768 | this.elt.playbackRate = val;
1769 | }
1770 | };
1771 |
1772 | /**
1773 | * If no arguments are given, returns the current time of the element.
1774 | * If an argument is given the current time of the element is set to it.
1775 | *
1776 | * @method time
1777 | * @param {Number} [time] time to jump to (in seconds)
1778 | * @return {Number|Object/p5.MediaElement} current time (in seconds)
1779 | * or p5.MediaElement
1780 | */
1781 | p5.MediaElement.prototype.time = function(val) {
1782 | if (typeof val === 'undefined') {
1783 | return this.elt.currentTime;
1784 | } else {
1785 | this.elt.currentTime = val;
1786 | }
1787 | };
1788 |
1789 | /**
1790 | * Returns the duration of the HTML5 media element.
1791 | *
1792 | * @method duration
1793 | * @return {Number} duration
1794 | */
1795 | p5.MediaElement.prototype.duration = function() {
1796 | return this.elt.duration;
1797 | };
1798 | p5.MediaElement.prototype.pixels = [];
1799 | p5.MediaElement.prototype.loadPixels = function() {
1800 | if (!this.canvas) {
1801 | this.canvas = document.createElement('canvas');
1802 | this.drawingContext = this.canvas.getContext('2d');
1803 | }
1804 | if (this.loadedmetadata) { // wait for metadata for w/h
1805 | if (this.canvas.width !== this.elt.width) {
1806 | this.canvas.width = this.elt.width;
1807 | this.canvas.height = this.elt.height;
1808 | this.width = this.canvas.width;
1809 | this.height = this.canvas.height;
1810 | }
1811 | this.drawingContext.drawImage(this.elt, 0, 0, this.canvas.width, this.canvas.height);
1812 | p5.Renderer2D.prototype.loadPixels.call(this);
1813 | }
1814 | return this;
1815 | }
1816 | p5.MediaElement.prototype.updatePixels = function(x, y, w, h){
1817 | if (this.loadedmetadata) { // wait for metadata
1818 | p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
1819 | }
1820 | return this;
1821 | }
1822 | p5.MediaElement.prototype.get = function(x, y, w, h){
1823 | if (this.loadedmetadata) { // wait for metadata
1824 | return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
1825 | } else if (!x) {
1826 | return new p5.Image(1, 1);
1827 | } else {
1828 | return [0, 0, 0, 255];
1829 | }
1830 | };
1831 | p5.MediaElement.prototype.set = function(x, y, imgOrCol){
1832 | if (this.loadedmetadata) { // wait for metadata
1833 | p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
1834 | }
1835 | };
1836 | p5.MediaElement.prototype.copy = function(){
1837 | p5.Renderer2D.prototype.copy.apply(this, arguments);
1838 | };
1839 | p5.MediaElement.prototype.mask = function(){
1840 | this.loadPixels();
1841 | p5.Image.prototype.mask.apply(this, arguments);
1842 | };
1843 | /**
1844 | * Schedule an event to be called when the audio or video
1845 | * element reaches the end. If the element is looping,
1846 | * this will not be called. The element is passed in
1847 | * as the argument to the onended callback.
1848 | *
1849 | * @method onended
1850 | * @param {Function} callback function to call when the
1851 | * soundfile has ended. The
1852 | * media element will be passed
1853 | * in as the argument to the
1854 | * callback.
1855 | * @return {Object/p5.MediaElement}
1856 | * @example
1857 | *
1858 | * function setup() {
1859 | * audioEl = createAudio('assets/beat.mp3');
1860 | * audioEl.showControls(true);
1861 | * audioEl.onended(sayDone);
1862 | * }
1863 | *
1864 | * function sayDone(elt) {
1865 | * alert('done playing ' + elt.src );
1866 | * }
1867 | *
1868 | */
1869 | p5.MediaElement.prototype.onended = function(callback) {
1870 | this._onended = callback;
1871 | return this;
1872 | };
1873 |
1874 |
1875 | /*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
1876 |
1877 | /**
1878 | * Send the audio output of this element to a specified audioNode or
1879 | * p5.sound object. If no element is provided, connects to p5's master
1880 | * output. That connection is established when this method is first called.
1881 | * All connections are removed by the .disconnect() method.
1882 | *
1883 | * This method is meant to be used with the p5.sound.js addon library.
1884 | *
1885 | * @method connect
1886 | * @param {AudioNode|p5.sound object} audioNode AudioNode from the Web Audio API,
1887 | * or an object from the p5.sound library
1888 | */
1889 | p5.MediaElement.prototype.connect = function(obj) {
1890 | var audioContext, masterOutput;
1891 |
1892 | // if p5.sound exists, same audio context
1893 | if (typeof p5.prototype.getAudioContext === 'function') {
1894 | audioContext = p5.prototype.getAudioContext();
1895 | masterOutput = p5.soundOut.input;
1896 | } else {
1897 | try {
1898 | audioContext = obj.context;
1899 | masterOutput = audioContext.destination
1900 | } catch(e) {
1901 | throw 'connect() is meant to be used with Web Audio API or p5.sound.js'
1902 | }
1903 | }
1904 |
1905 | // create a Web Audio MediaElementAudioSourceNode if none already exists
1906 | if (!this.audioSourceNode) {
1907 | this.audioSourceNode = audioContext.createMediaElementSource(this.elt);
1908 |
1909 | // connect to master output when this method is first called
1910 | this.audioSourceNode.connect(masterOutput);
1911 | }
1912 |
1913 | // connect to object if provided
1914 | if (obj) {
1915 | if (obj.input) {
1916 | this.audioSourceNode.connect(obj.input);
1917 | } else {
1918 | this.audioSourceNode.connect(obj);
1919 | }
1920 | }
1921 |
1922 | // otherwise connect to master output of p5.sound / AudioContext
1923 | else {
1924 | this.audioSourceNode.connect(masterOutput);
1925 | }
1926 |
1927 | };
1928 |
1929 | /**
1930 | * Disconnect all Web Audio routing, including to master output.
1931 | * This is useful if you want to re-route the output through
1932 | * audio effects, for example.
1933 | *
1934 | * @method disconnect
1935 | */
1936 | p5.MediaElement.prototype.disconnect = function() {
1937 | if (this.audioSourceNode) {
1938 | this.audioSourceNode.disconnect();
1939 | } else {
1940 | throw 'nothing to disconnect';
1941 | }
1942 | };
1943 |
1944 |
1945 | /*** SHOW / HIDE CONTROLS ***/
1946 |
1947 | /**
1948 | * Show the default MediaElement controls, as determined by the web browser.
1949 | *
1950 | * @method showControls
1951 | */
1952 | p5.MediaElement.prototype.showControls = function() {
1953 | // must set style for the element to show on the page
1954 | this.elt.style['text-align'] = 'inherit';
1955 | this.elt.controls = true;
1956 | };
1957 |
1958 | /**
1959 | * Hide the default mediaElement controls.
1960 | *
1961 | * @method hideControls
1962 | */
1963 | p5.MediaElement.prototype.hideControls = function() {
1964 | this.elt.controls = false;
1965 | };
1966 |
1967 | /*** SCHEDULE EVENTS ***/
1968 |
1969 | /**
1970 | * Schedule events to trigger every time a MediaElement
1971 | * (audio/video) reaches a playback cue point.
1972 | *
1973 | * Accepts a callback function, a time (in seconds) at which to trigger
1974 | * the callback, and an optional parameter for the callback.
1975 | *
1976 | * Time will be passed as the first parameter to the callback function,
1977 | * and param will be the second parameter.
1978 | *
1979 | *
1980 | * @method addCue
1981 | * @param {Number} time Time in seconds, relative to this media
1982 | * element's playback. For example, to trigger
1983 | * an event every time playback reaches two
1984 | * seconds, pass in the number 2. This will be
1985 | * passed as the first parameter to
1986 | * the callback function.
1987 | * @param {Function} callback Name of a function that will be
1988 | * called at the given time. The callback will
1989 | * receive time and (optionally) param as its
1990 | * two parameters.
1991 | * @param {Object} [value] An object to be passed as the
1992 | * second parameter to the
1993 | * callback function.
1994 | * @return {Number} id ID of this cue,
1995 | * useful for removeCue(id)
1996 | * @example
1997 | *
1998 | * function setup() {
1999 | * background(255,255,255);
2000 | *
2001 | * audioEl = createAudio('assets/beat.mp3');
2002 | * audioEl.showControls();
2003 | *
2004 | * // schedule three calls to changeBackground
2005 | * audioEl.addCue(0.5, changeBackground, color(255,0,0) );
2006 | * audioEl.addCue(1.0, changeBackground, color(0,255,0) );
2007 | * audioEl.addCue(2.5, changeBackground, color(0,0,255) );
2008 | * audioEl.addCue(3.0, changeBackground, color(0,255,255) );
2009 | * audioEl.addCue(4.2, changeBackground, color(255,255,0) );
2010 | * audioEl.addCue(5.0, changeBackground, color(255,255,0) );
2011 | * }
2012 | *
2013 | * function changeBackground(val) {
2014 | * background(val);
2015 | * }
2016 | *
2017 | */
2018 | p5.MediaElement.prototype.addCue = function(time, callback, val) {
2019 | var id = this._cueIDCounter++;
2020 |
2021 | var cue = new Cue(callback, time, id, val);
2022 | this._cues.push(cue);
2023 |
2024 | if (!this.elt.ontimeupdate) {
2025 | this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
2026 | }
2027 |
2028 | return id;
2029 | };
2030 |
2031 | /**
2032 | * Remove a callback based on its ID. The ID is returned by the
2033 | * addCue method.
2034 | *
2035 | * @method removeCue
2036 | * @param {Number} id ID of the cue, as returned by addCue
2037 | */
2038 | p5.MediaElement.prototype.removeCue = function(id) {
2039 | for (var i = 0; i < this._cues.length; i++) {
2040 | var cue = this._cues[i];
2041 | if (cue.id === id) {
2042 | this.cues.splice(i, 1);
2043 | }
2044 | }
2045 |
2046 | if (this._cues.length === 0) {
2047 | this.elt.ontimeupdate = null
2048 | }
2049 | };
2050 |
2051 | /**
2052 | * Remove all of the callbacks that had originally been scheduled
2053 | * via the addCue method.
2054 | *
2055 | * @method clearCues
2056 | */
2057 | p5.MediaElement.prototype.clearCues = function() {
2058 | this._cues = [];
2059 | this.elt.ontimeupdate = null;
2060 | };
2061 |
2062 | // private method that checks for cues to be fired if events
2063 | // have been scheduled using addCue(callback, time).
2064 | p5.MediaElement.prototype._onTimeUpdate = function() {
2065 | var playbackTime = this.time();
2066 |
2067 | for (var i = 0 ; i < this._cues.length; i++) {
2068 | var callbackTime = this._cues[i].time;
2069 | var val = this._cues[i].val;
2070 |
2071 |
2072 | if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
2073 |
2074 | // pass the scheduled callbackTime as parameter to the callback
2075 | this._cues[i].callback(val);
2076 | }
2077 |
2078 | }
2079 |
2080 | this._prevTime = playbackTime;
2081 | };
2082 |
2083 |
2084 | // Cue inspired by JavaScript setTimeout, and the
2085 | // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
2086 | var Cue = function(callback, time, id, val) {
2087 | this.callback = callback;
2088 | this.time = time;
2089 | this.id = id;
2090 | this.val = val;
2091 | };
2092 |
2093 | // =============================================================================
2094 | // p5.File
2095 | // =============================================================================
2096 |
2097 |
2098 | /**
2099 | * Base class for a file
2100 | * Using this for createFileInput
2101 | *
2102 | * @class p5.File
2103 | * @constructor
2104 | * @param {File} file File that is wrapped
2105 | * @param {Object} [pInst] pointer to p5 instance
2106 | */
2107 | p5.File = function(file, pInst) {
2108 | /**
2109 | * Underlying File object. All normal File methods can be called on this.
2110 | *
2111 | * @property file
2112 | */
2113 | this.file = file;
2114 |
2115 | this._pInst = pInst;
2116 |
2117 | // Splitting out the file type into two components
2118 | // This makes determining if image or text etc simpler
2119 | var typeList = file.type.split('/');
2120 | /**
2121 | * File type (image, text, etc.)
2122 | *
2123 | * @property type
2124 | */
2125 | this.type = typeList[0];
2126 | /**
2127 | * File subtype (usually the file extension jpg, png, xml, etc.)
2128 | *
2129 | * @property subtype
2130 | */
2131 | this.subtype = typeList[1];
2132 | /**
2133 | * File name
2134 | *
2135 | * @property name
2136 | */
2137 | this.name = file.name;
2138 | /**
2139 | * File size
2140 | *
2141 | * @property size
2142 | */
2143 | this.size = file.size;
2144 |
2145 | /**
2146 | * URL string containing image data.
2147 | *
2148 | * @property data
2149 | */
2150 | this.data = undefined;
2151 | };
2152 |
2153 | }));
2154 |
--------------------------------------------------------------------------------
/01_basics/sketch.js:
--------------------------------------------------------------------------------
1 | // Daniel Shiffman
2 | // Matter.js + p5.js Examples
3 | // This example is based on examples from: http://brm.io/matter-js/
4 |
5 |
6 | var Engine = Matter.Engine;
7 | var Render = Matter.Render;
8 | var World = Matter.World;
9 | var Bodies = Matter.Bodies;
10 | var Composite = Matter.Composite;
11 |
12 | var engine;
13 |
14 | var boxA;
15 | var boxB;
16 | var ground;
17 |
18 | function setup() {
19 | createCanvas(800, 600);
20 |
21 | // create an engine
22 | engine = Engine.create();
23 |
24 | // create two boxes and a ground
25 | boxA = Bodies.rectangle(400, 200, 80, 80);
26 | boxB = Bodies.rectangle(450, 50, 80, 80);
27 | ground = Bodies.rectangle(400, 610, 810, 60, {
28 | isStatic: true
29 | });
30 |
31 | // add all of the bodies to the world
32 | World.add(engine.world, [boxA, boxB, ground]);
33 |
34 | // run the engine
35 | Engine.run(engine);
36 | }
37 |
38 | // Using p5 to render
39 | function draw() {
40 | // I could ask for everything in the world
41 | // var bodies = Composite.allBodies(engine.world);
42 |
43 | background(51);
44 |
45 | // Basic demo
46 | // Getting vertices of each object
47 | var vertices = boxA.vertices;
48 | fill(255);
49 | beginShape();
50 | for (var i = 0; i < vertices.length; i++) {
51 | vertex(vertices[i].x, vertices[i].y);
52 | }
53 | endShape();
54 |
55 | // boxB vertices
56 | var vertices = boxB.vertices;
57 | fill(255);
58 | beginShape();
59 | for (var i = 0; i < vertices.length; i++) {
60 | vertex(vertices[i].x, vertices[i].y);
61 | }
62 | endShape();
63 |
64 | // Ground vertices
65 | var vertices = ground.vertices;
66 | beginShape();
67 | fill(127);
68 | for (var i = 0; i < vertices.length; i++) {
69 | vertex(vertices[i].x, vertices[i].y);
70 | }
71 | endShape();
72 | }
73 |
--------------------------------------------------------------------------------
/02_bouncing_balls/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/02_bouncing_balls/sketch.js:
--------------------------------------------------------------------------------
1 | // Daniel Shiffman
2 | // Matter.js + p5.js Examples
3 | // This example is based on examples from: http://brm.io/matter-js/
4 |
5 | var Engine = Matter.Engine;
6 | var Render = Matter.Render;
7 | var World = Matter.World;
8 | var Bodies = Matter.Bodies;
9 | var Composite = Matter.Composite;
10 | var Composites = Matter.Composites;
11 |
12 | var Mouse = Matter.Mouse;
13 | var MouseConstraint = Matter.MouseConstraint;
14 |
15 | var engine;
16 | var world;
17 | var bodies;
18 |
19 | var canvas;
20 |
21 | var mouseConstraint;
22 |
23 | function setup() {
24 | canvas = createCanvas(800, 600);
25 |
26 | // Mouse positions don't align
27 | // But it does work if I force pixel density of 1
28 | // pixelDensity(1);
29 | // Can I instead tell mouse to divide its xy by 2?
30 |
31 | // create an engine
32 | engine = Engine.create();
33 | world = engine.world;
34 |
35 | var mouse = Mouse.create(canvas.elt);
36 | var mouseParams = {
37 | mouse: mouse,
38 | constraint: {
39 | stiffness: 0.1,
40 | }
41 | }
42 | mouseConstraint = MouseConstraint.create(engine, mouseParams);
43 | mouseConstraint.mouse.pixelRatio = pixelDensity();
44 | World.add(world, mouseConstraint);
45 |
46 | var params = {
47 | isStatic: true
48 | }
49 | var ground = Bodies.rectangle(width / 2, height, width, 1, params);
50 | var wall1 = Bodies.rectangle(0, height / 2, 1, height, params);
51 | var wall2 = Bodies.rectangle(width, height / 2, 1, height, params);
52 | var top = Bodies.rectangle(width / 2, 0, width, 1, params);
53 | World.add(world, ground);
54 | World.add(world, wall1);
55 | World.add(world, wall2);
56 | World.add(world, top);
57 |
58 | function makeCircle(x, y) {
59 | var params = {
60 | restitution: 0.7,
61 | friction: 0.2
62 | }
63 | return Bodies.circle(x, y, 32, params);
64 | }
65 |
66 | // x, y, columns, rows, column gap, row gap
67 | //var stack = Composites.stack(20, 50, 15, 10, 20, 20, makeCircle);
68 | var stack = Composites.stack(20, height/2, 7, 3, 50, 50, makeCircle);
69 | bodies = stack.bodies;
70 |
71 | // add all of the bodies to the world
72 | World.add(world, stack);
73 |
74 | // run the engine
75 | Engine.run(engine);
76 | }
77 |
78 | function draw() {
79 | background(51);
80 | stroke(255);
81 | strokeWeight(1);
82 | fill(255, 50);
83 | for (var i = 0; i < bodies.length; i++) {
84 | var circle = bodies[i];
85 | var pos = circle.position;
86 | var r = circle.circleRadius;
87 | var angle = circle.angle;
88 | push();
89 | translate(pos.x, pos.y);
90 | rotate(angle);
91 | ellipse(0, 0, r * 2);
92 | line(0, 0, r, 0);
93 | pop();
94 | }
95 |
96 | var a = mouseConstraint.constraint.pointA;
97 | var bodyB = mouseConstraint.constraint.bodyB;
98 | if (bodyB) {
99 | strokeWeight(2);
100 | stroke(255);
101 | line(a.x, a.y, bodyB.position.x, bodyB.position.y);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/03_chain/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/03_chain/libraries/p5.dom.js:
--------------------------------------------------------------------------------
1 | /*! p5.dom.js v0.2.13 Oct 1, 2016 */
2 | /**
3 | * The web is much more than just canvas and p5.dom makes it easy to interact
4 | * with other HTML5 objects, including text, hyperlink, image, input, video,
5 | * audio, and webcam.
6 | * There is a set of creation methods, DOM manipulation methods, and
7 | * an extended p5.Element that supports a range of HTML elements. See the
8 | *
9 | * beyond the canvas tutorial for a full overview of how this addon works.
10 | *
11 | *
Methods and properties shown in black are part of the p5.js core, items in
12 | * blue are part of the p5.dom library. You will need to include an extra file
13 | * in order to access the blue functions. See the
14 | * using a library
15 | * section for information on how to include this library. p5.dom comes with
16 | * p5 complete or you can download the single file
17 | *
18 | * here.
19 | * See tutorial: beyond the canvas
20 | * for more info on how to use this libary.
21 | *
22 | * @module p5.dom
23 | * @submodule p5.dom
24 | * @for p5.dom
25 | * @main
26 | */
27 |
28 | (function (root, factory) {
29 | if (typeof define === 'function' && define.amd)
30 | define('p5.dom', ['p5'], function (p5) { (factory(p5));});
31 | else if (typeof exports === 'object')
32 | factory(require('../p5'));
33 | else
34 | factory(root['p5']);
35 | }(this, function (p5) {
36 | // =============================================================================
37 | // p5 additions
38 | // =============================================================================
39 |
40 | /**
41 | * Searches the page for an element with the given ID, class, or tag name (using the '#' or '.'
42 | * prefixes to specify an ID or class respectively, and none for a tag) and returns it as
43 | * a p5.Element. If a class or tag name is given with more than 1 element,
44 | * only the first element will be returned.
45 | * The DOM node itself can be accessed with .elt.
46 | * Returns null if none found. You can also specify a container to search within.
47 | *
48 | * @method select
49 | * @param {String} name id, class, or tag name of element to search for
50 | * @param {String} [container] id, p5.Element, or HTML element to search within
51 | * @return {Object/p5.Element|Null} p5.Element containing node found
52 | * @example
53 | *
54 | * function setup() {
55 | * createCanvas(100,100);
56 | * //translates canvas 50px down
57 | * select('canvas').position(100, 100);
58 | * }
59 | *
60 | *
61 | * // these are all valid calls to select()
62 | * var a = select('#moo');
63 | * var b = select('#blah', '#myContainer');
64 | * var c = select('#foo', b);
65 | * var d = document.getElementById('beep');
66 | * var e = select('p', d);
67 | *
68 | *
69 | */
70 | p5.prototype.select = function (e, p) {
71 | var res = null;
72 | var container = getContainer(p);
73 | if (e[0] === '.'){
74 | e = e.slice(1);
75 | res = container.getElementsByClassName(e);
76 | if (res.length) {
77 | res = res[0];
78 | } else {
79 | res = null;
80 | }
81 | }else if (e[0] === '#'){
82 | e = e.slice(1);
83 | res = container.getElementById(e);
84 | }else {
85 | res = container.getElementsByTagName(e);
86 | if (res.length) {
87 | res = res[0];
88 | } else {
89 | res = null;
90 | }
91 | }
92 | if (res) {
93 | return wrapElement(res);
94 | } else {
95 | return null;
96 | }
97 | };
98 |
99 | /**
100 | * Searches the page for elements with the given class or tag name (using the '.' prefix
101 | * to specify a class and no prefix for a tag) and returns them as p5.Elements
102 | * in an array.
103 | * The DOM node itself can be accessed with .elt.
104 | * Returns an empty array if none found.
105 | * You can also specify a container to search within.
106 | *
107 | * @method selectAll
108 | * @param {String} name class or tag name of elements to search for
109 | * @param {String} [container] id, p5.Element, or HTML element to search within
110 | * @return {Array} Array of p5.Elements containing nodes found
111 | * @example
112 | *
113 | * function setup() {
114 | * createButton('btn');
115 | * createButton('2nd btn');
116 | * createButton('3rd btn');
117 | * var buttons = selectAll('button');
118 | *
119 | * for (var i = 0; i < buttons.length; i++){
120 | * buttons[i].size(100,100);
121 | * }
122 | * }
123 | *
124 | *
125 | * // these are all valid calls to selectAll()
126 | * var a = selectAll('.moo');
127 | * var b = selectAll('div');
128 | * var c = selectAll('button', '#myContainer');
129 | * var d = select('#container');
130 | * var e = selectAll('p', d);
131 | * var f = document.getElementById('beep');
132 | * var g = select('.blah', f);
133 | *
134 | *
135 | */
136 | p5.prototype.selectAll = function (e, p) {
137 | var arr = [];
138 | var res;
139 | var container = getContainer(p);
140 | if (e[0] === '.'){
141 | e = e.slice(1);
142 | res = container.getElementsByClassName(e);
143 | } else {
144 | res = container.getElementsByTagName(e);
145 | }
146 | if (res) {
147 | for (var j = 0; j < res.length; j++) {
148 | var obj = wrapElement(res[j]);
149 | arr.push(obj);
150 | }
151 | }
152 | return arr;
153 | };
154 |
155 | /**
156 | * Helper function for select and selectAll
157 | */
158 | function getContainer(p) {
159 | var container = document;
160 | if (typeof p === 'string' && p[0] === '#'){
161 | p = p.slice(1);
162 | container = document.getElementById(p) || document;
163 | } else if (p instanceof p5.Element){
164 | container = p.elt;
165 | } else if (p instanceof HTMLElement){
166 | container = p;
167 | }
168 | return container;
169 | }
170 |
171 | /**
172 | * Helper function for getElement and getElements.
173 | */
174 | function wrapElement(elt) {
175 | if(elt.tagName === "INPUT" && elt.type === "checkbox") {
176 | var converted = new p5.Element(elt);
177 | converted.checked = function(){
178 | if (arguments.length === 0){
179 | return this.elt.checked;
180 | } else if(arguments[0]) {
181 | this.elt.checked = true;
182 | } else {
183 | this.elt.checked = false;
184 | }
185 | return this;
186 | };
187 | return converted;
188 | } else if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") {
189 | return new p5.MediaElement(elt);
190 | } else {
191 | return new p5.Element(elt);
192 | }
193 | }
194 |
195 | /**
196 | * Removes all elements created by p5, except any canvas / graphics
197 | * elements created by createCanvas or createGraphics.
198 | * Event handlers are removed, and element is removed from the DOM.
199 | * @method removeElements
200 | * @example
201 | *
202 | * function setup() {
203 | * createCanvas(100, 100);
204 | * createDiv('this is some text');
205 | * createP('this is a paragraph');
206 | * }
207 | * function mousePressed() {
208 | * removeElements(); // this will remove the div and p, not canvas
209 | * }
210 | *
211 | *
212 | */
213 | p5.prototype.removeElements = function (e) {
214 | for (var i=0; i
242 | * var myDiv;
243 | * function setup() {
244 | * myDiv = createDiv('this is some text');
245 | * }
246 | *
247 | */
248 |
249 | /**
250 | * Creates a <p></p> element in the DOM with given inner HTML. Used
251 | * for paragraph length text.
252 | * Appends to the container node if one is specified, otherwise
253 | * appends to body.
254 | *
255 | * @method createP
256 | * @param {String} html inner HTML for element created
257 | * @return {Object/p5.Element} pointer to p5.Element holding created node
258 | * @example
259 | *
260 | * var myP;
261 | * function setup() {
262 | * myP = createP('this is some text');
263 | * }
264 | *
265 | */
266 |
267 | /**
268 | * Creates a <span></span> element in the DOM with given inner HTML.
269 | * Appends to the container node if one is specified, otherwise
270 | * appends to body.
271 | *
272 | * @method createSpan
273 | * @param {String} html inner HTML for element created
274 | * @return {Object/p5.Element} pointer to p5.Element holding created node
275 | * @example
276 | *
277 | * var mySpan;
278 | * function setup() {
279 | * mySpan = createSpan('this is some text');
280 | * }
281 | *
282 | */
283 | var tags = ['div', 'p', 'span'];
284 | tags.forEach(function(tag) {
285 | var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1);
286 | p5.prototype[method] = function(html) {
287 | var elt = document.createElement(tag);
288 | elt.innerHTML = typeof html === undefined ? "" : html;
289 | return addElement(elt, this);
290 | }
291 | });
292 |
293 | /**
294 | * Creates an <img /> element in the DOM with given src and
295 | * alternate text.
296 | * Appends to the container node if one is specified, otherwise
297 | * appends to body.
298 | *
299 | * @method createImg
300 | * @param {String} src src path or url for image
301 | * @param {String} [alt] alternate text to be used if image does not load
302 | * @param {Function} [successCallback] callback to be called once image data is loaded
303 | * @return {Object/p5.Element} pointer to p5.Element holding created node
304 | * @example
305 | *
306 | * var img;
307 | * function setup() {
308 | * img = createImg('http://p5js.org/img/asterisk-01.png');
309 | * }
310 | *
311 | */
312 | p5.prototype.createImg = function() {
313 | var elt = document.createElement('img');
314 | var args = arguments;
315 | var self;
316 | var setAttrs = function(){
317 | self.width = elt.offsetWidth;
318 | self.height = elt.offsetHeight;
319 | if (args.length > 1 && typeof args[1] === 'function'){
320 | self.fn = args[1];
321 | self.fn();
322 | }else if (args.length > 1 && typeof args[2] === 'function'){
323 | self.fn = args[2];
324 | self.fn();
325 | }
326 | };
327 | elt.src = args[0];
328 | if (args.length > 1 && typeof args[1] === 'string'){
329 | elt.alt = args[1];
330 | }
331 | elt.onload = function(){
332 | setAttrs();
333 | }
334 | self = addElement(elt, this);
335 | return self;
336 | };
337 |
338 | /**
339 | * Creates an <a></a> element in the DOM for including a hyperlink.
340 | * Appends to the container node if one is specified, otherwise
341 | * appends to body.
342 | *
343 | * @method createA
344 | * @param {String} href url of page to link to
345 | * @param {String} html inner html of link element to display
346 | * @param {String} [target] target where new link should open,
347 | * could be _blank, _self, _parent, _top.
348 | * @return {Object/p5.Element} pointer to p5.Element holding created node
349 | * @example
350 | *
351 | * var myLink;
352 | * function setup() {
353 | * myLink = createA('http://p5js.org/', 'this is a link');
354 | * }
355 | *
356 | */
357 | p5.prototype.createA = function(href, html, target) {
358 | var elt = document.createElement('a');
359 | elt.href = href;
360 | elt.innerHTML = html;
361 | if (target) elt.target = target;
362 | return addElement(elt, this);
363 | };
364 |
365 | /** INPUT **/
366 |
367 |
368 | /**
369 | * Creates a slider <input></input> element in the DOM.
370 | * Use .size() to set the display length of the slider.
371 | * Appends to the container node if one is specified, otherwise
372 | * appends to body.
373 | *
374 | * @method createSlider
375 | * @param {Number} min minimum value of the slider
376 | * @param {Number} max maximum value of the slider
377 | * @param {Number} [value] default value of the slider
378 | * @param {Number} [step] step size for each tick of the slider (if step is set to 0, the slider will move continuously from the minimum to the maximum value)
379 | * @return {Object/p5.Element} pointer to p5.Element holding created node
380 | * @example
381 | *
382 | * var slider;
383 | * function setup() {
384 | * slider = createSlider(0, 255, 100);
385 | * slider.position(10, 10);
386 | * slider.style('width', '80px');
387 | * }
388 | *
389 | * function draw() {
390 | * var val = slider.value();
391 | * background(val);
392 | * }
393 | *
394 | *
395 | *
396 | * var slider;
397 | * function setup() {
398 | * colorMode(HSB);
399 | * slider = createSlider(0, 360, 60, 40);
400 | * slider.position(10, 10);
401 | * slider.style('width', '80px');
402 | * }
403 | *
404 | * function draw() {
405 | * var val = slider.value();
406 | * background(val, 100, 100, 1);
407 | * }
408 | *
409 | */
410 | p5.prototype.createSlider = function(min, max, value, step) {
411 | var elt = document.createElement('input');
412 | elt.type = 'range';
413 | elt.min = min;
414 | elt.max = max;
415 | if (step === 0) {
416 | elt.step = .000000000000000001; // smallest valid step
417 | } else if (step) {
418 | elt.step = step;
419 | }
420 | if (typeof(value) === "number") elt.value = value;
421 | return addElement(elt, this);
422 | };
423 |
424 | /**
425 | * Creates a <button></button> element in the DOM.
426 | * Use .size() to set the display size of the button.
427 | * Use .mousePressed() to specify behavior on press.
428 | * Appends to the container node if one is specified, otherwise
429 | * appends to body.
430 | *
431 | * @method createButton
432 | * @param {String} label label displayed on the button
433 | * @param {String} [value] value of the button
434 | * @return {Object/p5.Element} pointer to p5.Element holding created node
435 | * @example
436 | *
437 | * var button;
438 | * function setup() {
439 | * createCanvas(100, 100);
440 | * background(0);
441 | * button = createButton('click me');
442 | * button.position(19, 19);
443 | * button.mousePressed(changeBG);
444 | * }
445 | *
446 | * function changeBG() {
447 | * var val = random(255);
448 | * background(val);
449 | * }
450 | *
451 | */
452 | p5.prototype.createButton = function(label, value) {
453 | var elt = document.createElement('button');
454 | elt.innerHTML = label;
455 | elt.value = value;
456 | if (value) elt.value = value;
457 | return addElement(elt, this);
458 | };
459 |
460 | /**
461 | * Creates a checkbox <input></input> element in the DOM.
462 | * Calling .checked() on a checkbox returns if it is checked or not
463 | *
464 | * @method createCheckbox
465 | * @param {String} [label] label displayed after checkbox
466 | * @param {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given
467 | * @return {Object/p5.Element} pointer to p5.Element holding created node
468 | * @example
469 | *
470 | * var checkbox;
471 | *
472 | * function setup() {
473 | * checkbox = createCheckbox('label', false);
474 | * checkbox.changed(myCheckedEvent);
475 | * }
476 | *
477 | * function myCheckedEvent() {
478 | * if (this.checked()) {
479 | * console.log("Checking!");
480 | * } else {
481 | * console.log("Unchecking!");
482 | * }
483 | * }
484 | *
485 | */
486 | p5.prototype.createCheckbox = function() {
487 | var elt = document.createElement('div');
488 | var checkbox = document.createElement('input');
489 | checkbox.type = 'checkbox';
490 | elt.appendChild(checkbox);
491 | //checkbox must be wrapped in p5.Element before label so that label appears after
492 | var self = addElement(elt, this);
493 | self.checked = function(){
494 | var cb = self.elt.getElementsByTagName('input')[0];
495 | if (cb) {
496 | if (arguments.length === 0){
497 | return cb.checked;
498 | }else if(arguments[0]){
499 | cb.checked = true;
500 | }else{
501 | cb.checked = false;
502 | }
503 | }
504 | return self;
505 | };
506 | this.value = function(val){
507 | self.value = val;
508 | return this;
509 | };
510 | if (arguments[0]){
511 | var ran = Math.random().toString(36).slice(2);
512 | var label = document.createElement('label');
513 | checkbox.setAttribute('id', ran);
514 | label.htmlFor = ran;
515 | self.value(arguments[0]);
516 | label.appendChild(document.createTextNode(arguments[0]));
517 | elt.appendChild(label);
518 | }
519 | if (arguments[1]){
520 | checkbox.checked = true;
521 | }
522 | return self;
523 | };
524 |
525 | /**
526 | * Creates a dropdown menu <select></select> element in the DOM.
527 | * @method createSelect
528 | * @param {boolean} [multiple] [true if dropdown should support multiple selections]
529 | * @return {Object/p5.Element} pointer to p5.Element holding created node
530 | * @example
531 | *
532 | * var sel;
533 | *
534 | * function setup() {
535 | * textAlign(CENTER);
536 | * background(200);
537 | * sel = createSelect();
538 | * sel.position(10, 10);
539 | * sel.option('pear');
540 | * sel.option('kiwi');
541 | * sel.option('grape');
542 | * sel.changed(mySelectEvent);
543 | * }
544 | *
545 | * function mySelectEvent() {
546 | * var item = sel.value();
547 | * background(200);
548 | * text("it's a "+item+"!", 50, 50);
549 | * }
550 | *
551 | */
552 | p5.prototype.createSelect = function(mult) {
553 | var elt = document.createElement('select');
554 | if (mult){
555 | elt.setAttribute('multiple', 'true');
556 | }
557 | var self = addElement(elt, this);
558 | self.option = function(name, value){
559 | var opt = document.createElement('option');
560 | opt.innerHTML = name;
561 | if (arguments.length > 1)
562 | opt.value = value;
563 | else
564 | opt.value = name;
565 | elt.appendChild(opt);
566 | };
567 | self.selected = function(value){
568 | var arr = [];
569 | if (arguments.length > 0){
570 | for (var i = 0; i < this.elt.length; i++){
571 | if (value.toString() === this.elt[i].value){
572 | this.elt.selectedIndex = i;
573 | }
574 | }
575 | return this;
576 | }else{
577 | if (mult){
578 | for (var i = 0; i < this.elt.selectedOptions.length; i++){
579 | arr.push(this.elt.selectedOptions[i].value);
580 | }
581 | return arr;
582 | }else{
583 | return this.elt.value;
584 | }
585 | }
586 | };
587 | return self;
588 | };
589 |
590 | /**
591 | * Creates a radio button <input></input> element in the DOM.
592 | * The .option() method can be used to set options for the radio after it is
593 | * created. The .value() method will return the currently selected option.
594 | *
595 | * @method createRadio
596 | * @param {String} [divId] the id and name of the created div and input field respectively
597 | * @return {Object/p5.Element} pointer to p5.Element holding created node
598 | * @example
599 | *
600 | * var radio;
601 | *
602 | * function setup() {
603 | * radio = createRadio();
604 | * radio.option("black");
605 | * radio.option("white");
606 | * radio.option("gray");
607 | * radio.style('width', '60px');
608 | * textAlign(CENTER);
609 | * fill(255, 0, 0);
610 | * }
611 | *
612 | * function draw() {
613 | * var val = radio.value();
614 | * background(val);
615 | * text(val, width/2, height/2);
616 | * }
617 | *
618 | *
619 | * var radio;
620 | *
621 | * function setup() {
622 | * radio = createRadio();
623 | * radio.option('apple', 1);
624 | * radio.option('bread', 2);
625 | * radio.option('juice', 3);
626 | * radio.style('width', '60px');
627 | * textAlign(CENTER);
628 | * }
629 | *
630 | * function draw() {
631 | * background(200);
632 | * var val = radio.value();
633 | * if (val) {
634 | * text('item cost is $'+val, width/2, height/2);
635 | * }
636 | * }
637 | *
638 | */
639 | p5.prototype.createRadio = function() {
640 | var radios = document.querySelectorAll("input[type=radio]");
641 | var count = 0;
642 | if(radios.length > 1){
643 | var length = radios.length;
644 | var prev=radios[0].name;
645 | var current = radios[1].name;
646 | count = 1;
647 | for(var i = 1; i < length; i++) {
648 | current = radios[i].name;
649 | if(prev != current){
650 | count++;
651 | }
652 | prev = current;
653 | }
654 | }
655 | else if (radios.length == 1){
656 | count = 1;
657 | }
658 | var elt = document.createElement('div');
659 | var self = addElement(elt, this);
660 | var times = -1;
661 | self.option = function(name, value){
662 | var opt = document.createElement('input');
663 | opt.type = 'radio';
664 | opt.innerHTML = name;
665 | if (arguments.length > 1)
666 | opt.value = value;
667 | else
668 | opt.value = name;
669 | opt.setAttribute('name',"defaultradio"+count);
670 | elt.appendChild(opt);
671 | if (name){
672 | times++;
673 | var ran = Math.random().toString(36).slice(2);
674 | var label = document.createElement('label');
675 | opt.setAttribute('id', "defaultradio"+count+"-"+times);
676 | label.htmlFor = "defaultradio"+count+"-"+times;
677 | label.appendChild(document.createTextNode(name));
678 | elt.appendChild(label);
679 | }
680 | return opt;
681 | };
682 | self.selected = function(){
683 | var length = this.elt.childNodes.length;
684 | if(arguments.length == 1) {
685 | for (var i = 0; i < length; i+=2){
686 | if(this.elt.childNodes[i].value == arguments[0])
687 | this.elt.childNodes[i].checked = true;
688 | }
689 | return this;
690 | } else {
691 | for (var i = 0; i < length; i+=2){
692 | if(this.elt.childNodes[i].checked == true)
693 | return this.elt.childNodes[i].value;
694 | }
695 | }
696 | };
697 | self.value = function(){
698 | var length = this.elt.childNodes.length;
699 | if(arguments.length == 1) {
700 | for (var i = 0; i < length; i+=2){
701 | if(this.elt.childNodes[i].value == arguments[0])
702 | this.elt.childNodes[i].checked = true;
703 | }
704 | return this;
705 | } else {
706 | for (var i = 0; i < length; i+=2){
707 | if(this.elt.childNodes[i].checked == true)
708 | return this.elt.childNodes[i].value;
709 | }
710 | return "";
711 | }
712 | };
713 | return self
714 | };
715 |
716 | /**
717 | * Creates an <input></input> element in the DOM for text input.
718 | * Use .size() to set the display length of the box.
719 | * Appends to the container node if one is specified, otherwise
720 | * appends to body.
721 | *
722 | * @method createInput
723 | * @param {Number} [value] default value of the input box
724 | * @return {Object/p5.Element} pointer to p5.Element holding created node
725 | * @example
726 | *
727 | * function setup(){
728 | * var inp = createInput('');
729 | * inp.input(myInputEvent);
730 | * }
731 | *
732 | * function myInputEvent(){
733 | * console.log('you are typing: ', this.value());
734 | * }
735 | *
736 | *
737 | */
738 | p5.prototype.createInput = function(value) {
739 | var elt = document.createElement('input');
740 | elt.type = 'text';
741 | if (value) elt.value = value;
742 | return addElement(elt, this);
743 | };
744 |
745 | /**
746 | * Creates an <input></input> element in the DOM of type 'file'.
747 | * This allows users to select local files for use in a sketch.
748 | *
749 | * @method createFileInput
750 | * @param {Function} [callback] callback function for when a file loaded
751 | * @param {String} [multiple] optional to allow multiple files selected
752 | * @return {Object/p5.Element} pointer to p5.Element holding created DOM element
753 | */
754 | p5.prototype.createFileInput = function(callback, multiple) {
755 |
756 | // Is the file stuff supported?
757 | if (window.File && window.FileReader && window.FileList && window.Blob) {
758 | // Yup, we're ok and make an input file selector
759 | var elt = document.createElement('input');
760 | elt.type = 'file';
761 |
762 | // If we get a second argument that evaluates to true
763 | // then we are looking for multiple files
764 | if (multiple) {
765 | // Anything gets the job done
766 | elt.multiple = 'multiple';
767 | }
768 |
769 | // Function to handle when a file is selected
770 | // We're simplifying life and assuming that we always
771 | // want to load every selected file
772 | function handleFileSelect(evt) {
773 | // These are the files
774 | var files = evt.target.files;
775 | // Load each one and trigger a callback
776 | for (var i = 0; i < files.length; i++) {
777 | var f = files[i];
778 | var reader = new FileReader();
779 | function makeLoader(theFile) {
780 | // Making a p5.File object
781 | var p5file = new p5.File(theFile);
782 | return function(e) {
783 | p5file.data = e.target.result;
784 | callback(p5file);
785 | };
786 | };
787 | reader.onload = makeLoader(f);
788 |
789 | // Text or data?
790 | // This should likely be improved
791 | if (f.type.indexOf('text') > -1) {
792 | reader.readAsText(f);
793 | } else {
794 | reader.readAsDataURL(f);
795 | }
796 | }
797 | }
798 |
799 | // Now let's handle when a file was selected
800 | elt.addEventListener('change', handleFileSelect, false);
801 | return addElement(elt, this);
802 | } else {
803 | console.log('The File APIs are not fully supported in this browser. Cannot create element.');
804 | }
805 | };
806 |
807 |
808 | /** VIDEO STUFF **/
809 |
810 | function createMedia(pInst, type, src, callback) {
811 | var elt = document.createElement(type);
812 |
813 | // allow src to be empty
814 | var src = src || '';
815 | if (typeof src === 'string') {
816 | src = [src];
817 | }
818 | for (var i=0; ithis
854 | * page for further information about supported formats.
855 | *
856 | * @method createVideo
857 | * @param {String|Array} src path to a video file, or array of paths for
858 | * supporting different browsers
859 | * @param {Object} [callback] callback function to be called upon
860 | * 'canplaythrough' event fire, that is, when the
861 | * browser can play the media, and estimates that
862 | * enough data has been loaded to play the media
863 | * up to its end without having to stop for
864 | * further buffering of content
865 | * @return {Object/p5.Element} pointer to video p5.Element
866 | */
867 | p5.prototype.createVideo = function(src, callback) {
868 | return createMedia(this, 'video', src, callback);
869 | };
870 |
871 | /** AUDIO STUFF **/
872 |
873 | /**
874 | * Creates a hidden HTML5 <audio> element in the DOM for simple audio
875 | * playback. Appends to the container node if one is specified,
876 | * otherwise appends to body. The first parameter
877 | * can be either a single string path to a audio file, or an array of string
878 | * paths to different formats of the same audio. This is useful for ensuring
879 | * that your audio can play across different browsers, as each supports
880 | * different formats. See this
881 | * page for further information about supported formats.
882 | *
883 | * @method createAudio
884 | * @param {String|Array} src path to an audio file, or array of paths for
885 | * supporting different browsers
886 | * @param {Object} [callback] callback function to be called upon
887 | * 'canplaythrough' event fire, that is, when the
888 | * browser can play the media, and estimates that
889 | * enough data has been loaded to play the media
890 | * up to its end without having to stop for
891 | * further buffering of content
892 | * @return {Object/p5.Element} pointer to audio p5.Element
893 | */
894 | p5.prototype.createAudio = function(src, callback) {
895 | return createMedia(this, 'audio', src, callback);
896 | };
897 |
898 |
899 | /** CAMERA STUFF **/
900 |
901 | p5.prototype.VIDEO = 'video';
902 | p5.prototype.AUDIO = 'audio';
903 |
904 | navigator.getUserMedia = navigator.getUserMedia ||
905 | navigator.webkitGetUserMedia ||
906 | navigator.mozGetUserMedia ||
907 | navigator.msGetUserMedia;
908 |
909 | /**
910 | * Creates a new <video> element that contains the audio/video feed
911 | * from a webcam. This can be drawn onto the canvas using video().
912 | * More specific properties of the feed can be passing in a Constraints object.
913 | * See the
914 | * W3C
915 | * spec for possible properties. Note that not all of these are supported
916 | * by all browsers.
917 | * Security note: A new browser security specification requires that getUserMedia,
918 | * which is behind createCapture(), only works when you're running the code locally,
919 | * or on HTTPS. Learn more here
920 | * and here.
921 | *
922 | * @method createCapture
923 | * @param {String|Constant|Object} type type of capture, either VIDEO or
924 | * AUDIO if none specified, default both,
925 | * or a Constraints object
926 | * @param {Function} callback function to be called once
927 | * stream has loaded
928 | * @return {Object/p5.Element} capture video p5.Element
929 | * @example
930 | *
931 | * var capture;
932 | *
933 | * function setup() {
934 | * createCanvas(480, 120);
935 | * capture = createCapture(VIDEO);
936 | * }
937 | *
938 | * function draw() {
939 | * image(capture, 0, 0, width, width*capture.height/capture.width);
940 | * filter(INVERT);
941 | * }
942 | *
943 | *
944 | * function setup() {
945 | * createCanvas(480, 120);
946 | * var constraints = {
947 | * video: {
948 | * mandatory: {
949 | * minWidth: 1280,
950 | * minHeight: 720
951 | * },
952 | * optional: [
953 | * { maxFrameRate: 10 }
954 | * ]
955 | * },
956 | * audio: true
957 | * };
958 | * createCapture(constraints, function(stream) {
959 | * console.log(stream);
960 | * });
961 | * }
962 | *
963 | */
964 | p5.prototype.createCapture = function() {
965 | var useVideo = true;
966 | var useAudio = true;
967 | var constraints;
968 | var cb;
969 | for (var i=0; i
1020 | * var h2 = createElement('h2','im an h2 p5.element!');
1021 | *
1022 | */
1023 | p5.prototype.createElement = function(tag, content) {
1024 | var elt = document.createElement(tag);
1025 | if (typeof content !== 'undefined') {
1026 | elt.innerHTML = content;
1027 | }
1028 | return addElement(elt, this);
1029 | };
1030 |
1031 |
1032 | // =============================================================================
1033 | // p5.Element additions
1034 | // =============================================================================
1035 | /**
1036 | *
1037 | * Adds specified class to the element.
1038 | *
1039 | * @for p5.Element
1040 | * @method addClass
1041 | * @param {String} class name of class to add
1042 | * @return {Object/p5.Element}
1043 | * @example
1044 | *
1045 | * var div = createDiv('div');
1046 | * div.addClass('myClass');
1047 | *
1048 | */
1049 | p5.Element.prototype.addClass = function(c) {
1050 | if (this.elt.className) {
1051 | // PEND don't add class more than once
1052 | //var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?');
1053 | //if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) {
1054 | this.elt.className = this.elt.className+' '+c;
1055 | //}
1056 | } else {
1057 | this.elt.className = c;
1058 | }
1059 | return this;
1060 | }
1061 |
1062 | /**
1063 | *
1064 | * Removes specified class from the element.
1065 | *
1066 | * @method removeClass
1067 | * @param {String} class name of class to remove
1068 | * @return {Object/p5.Element}
1069 | */
1070 | p5.Element.prototype.removeClass = function(c) {
1071 | var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)');
1072 | this.elt.className = this.elt.className.replace(regex, '');
1073 | this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional)
1074 | return this;
1075 | }
1076 |
1077 | /**
1078 | *
1079 | * Attaches the element as a child to the parent specified.
1080 | * Accepts either a string ID, DOM node, or p5.Element.
1081 | * If no argument is specified, an array of children DOM nodes is returned.
1082 | *
1083 | * @method child
1084 | * @param {String|Object|p5.Element} [child] the ID, DOM node, or p5.Element
1085 | * to add to the current element
1086 | * @return {p5.Element}
1087 | * @example
1088 | *
1089 | * var div0 = createDiv('this is the parent');
1090 | * var div1 = createDiv('this is the child');
1091 | * div0.child(div1); // use p5.Element
1092 | *
1093 | *
1094 | * var div0 = createDiv('this is the parent');
1095 | * var div1 = createDiv('this is the child');
1096 | * div1.id('apples');
1097 | * div0.child('apples'); // use id
1098 | *
1099 | *
1100 | * var div0 = createDiv('this is the parent');
1101 | * var elt = document.getElementById('myChildDiv');
1102 | * div0.child(elt); // use element from page
1103 | *
1104 | */
1105 | p5.Element.prototype.child = function(c) {
1106 | if (c === null){
1107 | return this.elt.childNodes
1108 | }
1109 | if (typeof c === 'string') {
1110 | if (c[0] === '#') {
1111 | c = c.substring(1);
1112 | }
1113 | c = document.getElementById(c);
1114 | } else if (c instanceof p5.Element) {
1115 | c = c.elt;
1116 | }
1117 | this.elt.appendChild(c);
1118 | return this;
1119 | };
1120 |
1121 | /**
1122 | * Centers a p5 Element either vertically, horizontally,
1123 | * or both, relative to its parent or according to
1124 | * the body if the Element has no parent. If no argument is passed
1125 | * the Element is aligned both vertically and horizontally.
1126 | *
1127 | * @param {String} align passing 'vertical', 'horizontal' aligns element accordingly
1128 | * @return {Object/p5.Element} pointer to p5.Element
1129 | * @example
1130 | *
1131 | * function setup() {
1132 | * var div = createDiv('').size(10,10);
1133 | * div.style('background-color','orange');
1134 | * div.center();
1135 | *
1136 | * }
1137 | *
1138 | */
1139 | p5.Element.prototype.center = function(align) {
1140 | var style = this.elt.style.display;
1141 | var hidden = this.elt.style.display === 'none';
1142 | var parentHidden = this.parent().style.display === 'none';
1143 | var pos = { x : this.elt.offsetLeft, y : this.elt.offsetTop };
1144 |
1145 | if (hidden) this.show();
1146 |
1147 | this.elt.style.display = 'block';
1148 | this.position(0,0);
1149 |
1150 | if (parentHidden) this.parent().style.display = 'block';
1151 |
1152 | var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth);
1153 | var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight);
1154 | var y = pos.y;
1155 | var x = pos.x;
1156 |
1157 | if (align === 'both' || align === undefined){
1158 | this.position(wOffset/2, hOffset/2);
1159 | }else if (align === 'horizontal'){
1160 | this.position(wOffset/2, y);
1161 | }else if (align === 'vertical'){
1162 | this.position(x, hOffset/2);
1163 | }
1164 |
1165 | this.style('display', style);
1166 |
1167 | if (hidden) this.hide();
1168 |
1169 | if (parentHidden) this.parent().style.display = 'none';
1170 |
1171 | return this;
1172 | };
1173 |
1174 | /**
1175 | *
1176 | * If an argument is given, sets the inner HTML of the element,
1177 | * replacing any existing html. If no arguments are given, returns
1178 | * the inner HTML of the element.
1179 | *
1180 | * @for p5.Element
1181 | * @method html
1182 | * @param {String} [html] the HTML to be placed inside the element
1183 | * @return {Object/p5.Element|String}
1184 | * @example
1185 | *
1186 | * var div = createDiv('').size(100,100);
1187 | * div.style('background-color','orange');
1188 | * div.html('hi');
1189 | *
1190 | */
1191 | p5.Element.prototype.html = function(html) {
1192 | if (typeof html !== 'undefined') {
1193 | this.elt.innerHTML = html;
1194 | return this;
1195 | } else {
1196 | return this.elt.innerHTML;
1197 | }
1198 | };
1199 |
1200 | /**
1201 | *
1202 | * Sets the position of the element relative to (0, 0) of the
1203 | * window. Essentially, sets position:absolute and left and top
1204 | * properties of style. If no arguments given returns the x and y position
1205 | * of the element in an object.
1206 | *
1207 | * @method position
1208 | * @param {Number} [x] x-position relative to upper left of window
1209 | * @param {Number} [y] y-position relative to upper left of window
1210 | * @return {Object/p5.Element}
1211 | * @example
1212 | *
1213 | * function setup() {
1214 | * var cnv = createCanvas(100, 100);
1215 | * // positions canvas 50px to the right and 100px
1216 | * // below upper left corner of the window
1217 | * cnv.position(50, 100);
1218 | * }
1219 | *
1220 | */
1221 | p5.Element.prototype.position = function() {
1222 | if (arguments.length === 0){
1223 | return { 'x' : this.elt.offsetLeft , 'y' : this.elt.offsetTop };
1224 | }else{
1225 | this.elt.style.position = 'absolute';
1226 | this.elt.style.left = arguments[0]+'px';
1227 | this.elt.style.top = arguments[1]+'px';
1228 | this.x = arguments[0];
1229 | this.y = arguments[1];
1230 | return this;
1231 | }
1232 | };
1233 |
1234 | /* Helper method called by p5.Element.style() */
1235 | p5.Element.prototype._translate = function(){
1236 | this.elt.style.position = 'absolute';
1237 | // save out initial non-translate transform styling
1238 | var transform = '';
1239 | if (this.elt.style.transform) {
1240 | transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
1241 | transform = transform.replace(/translate[X-Z]?\(.*\)/g, '');
1242 | }
1243 | if (arguments.length === 2) {
1244 | this.elt.style.transform = 'translate('+arguments[0]+'px, '+arguments[1]+'px)';
1245 | } else if (arguments.length > 2) {
1246 | this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)';
1247 | if (arguments.length === 3) {
1248 | this.elt.parentElement.style.perspective = '1000px';
1249 | } else {
1250 | this.elt.parentElement.style.perspective = arguments[3]+'px';
1251 | }
1252 | }
1253 | // add any extra transform styling back on end
1254 | this.elt.style.transform += transform;
1255 | return this;
1256 | };
1257 |
1258 | /* Helper method called by p5.Element.style() */
1259 | p5.Element.prototype._rotate = function(){
1260 | // save out initial non-rotate transform styling
1261 | var transform = '';
1262 | if (this.elt.style.transform) {
1263 | var transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
1264 | transform = transform.replace(/rotate[X-Z]?\(.*\)/g, '');
1265 | }
1266 |
1267 | if (arguments.length === 1){
1268 | this.elt.style.transform = 'rotate('+arguments[0]+'deg)';
1269 | }else if (arguments.length === 2){
1270 | this.elt.style.transform = 'rotate('+arguments[0]+'deg, '+arguments[1]+'deg)';
1271 | }else if (arguments.length === 3){
1272 | this.elt.style.transform = 'rotateX('+arguments[0]+'deg)';
1273 | this.elt.style.transform += 'rotateY('+arguments[1]+'deg)';
1274 | this.elt.style.transform += 'rotateZ('+arguments[2]+'deg)';
1275 | }
1276 | // add remaining transform back on
1277 | this.elt.style.transform += transform;
1278 | return this;
1279 | };
1280 |
1281 | /**
1282 | * Sets the given style (css) property (1st arg) of the element with the
1283 | * given value (2nd arg). If a single argument is given, .style()
1284 | * returns the value of the given property; however, if the single argument
1285 | * is given in css syntax ('text-align:center'), .style() sets the css
1286 | * appropriatly. .style() also handles 2d and 3d css transforms. If
1287 | * the 1st arg is 'rotate', 'translate', or 'position', the following arguments
1288 | * accept Numbers as values. ('translate', 10, 100, 50);
1289 | *
1290 | * @method style
1291 | * @param {String} property property to be set
1292 | * @param {String|Number|p5.Color} [value] value to assign to property
1293 | * @param {String|Number} [value] value to assign to property (rotate/translate)
1294 | * @param {String|Number} [value] value to assign to property (rotate/translate)
1295 | * @param {String|Number} [value] value to assign to property (translate)
1296 | * @return {String|Object/p5.Element} value of property, if no value is specified
1297 | * or p5.Element
1298 | * @example
1299 | *
1300 | * var myDiv = createDiv("I like pandas.");
1301 | * myDiv.style("font-size", "18px");
1302 | * myDiv.style("color", "#ff0000");
1303 | *
1304 | *
1305 | * var col = color(25,23,200,50);
1306 | * var button = createButton("button");
1307 | * button.style("background-color", col);
1308 | * button.position(10, 10);
1309 | *
1310 | *
1311 | * var myDiv = createDiv("I like lizards.");
1312 | * myDiv.style("position", 20, 20);
1313 | * myDiv.style("rotate", 45);
1314 | *
1315 | *
1316 | * var myDiv;
1317 | * function setup() {
1318 | * background(200);
1319 | * myDiv = createDiv("I like gray.");
1320 | * myDiv.position(20, 20);
1321 | * }
1322 | *
1323 | * function draw() {
1324 | * myDiv.style("font-size", mouseX+"px");
1325 | * }
1326 | *
1327 | */
1328 | p5.Element.prototype.style = function(prop, val) {
1329 | var self = this;
1330 |
1331 | if (val instanceof p5.Color) {
1332 | val = 'rgba(' + val.levels[0] + ',' + val.levels[1] + ',' + val.levels[2] + ',' + val.levels[3]/255 + ')'
1333 | }
1334 |
1335 | if (typeof val === 'undefined') {
1336 | if (prop.indexOf(':') === -1) {
1337 | var styles = window.getComputedStyle(self.elt);
1338 | var style = styles.getPropertyValue(prop);
1339 | return style;
1340 | } else {
1341 | var attrs = prop.split(';');
1342 | for (var i = 0; i < attrs.length; i++) {
1343 | var parts = attrs[i].split(':');
1344 | if (parts[0] && parts[1]) {
1345 | this.elt.style[parts[0].trim()] = parts[1].trim();
1346 | }
1347 | }
1348 | }
1349 | } else {
1350 | if (prop === 'rotate' || prop === 'translate' || prop === 'position'){
1351 | var trans = Array.prototype.shift.apply(arguments);
1352 | var f = this[trans] || this['_'+trans];
1353 | f.apply(this, arguments);
1354 | } else {
1355 | this.elt.style[prop] = val;
1356 | if (prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top') {
1357 | var numVal = val.replace(/\D+/g, '');
1358 | this[prop] = parseInt(numVal, 10); // pend: is this necessary?
1359 | }
1360 | }
1361 | }
1362 | return this;
1363 | };
1364 |
1365 |
1366 | /**
1367 | *
1368 | * Adds a new attribute or changes the value of an existing attribute
1369 | * on the specified element. If no value is specified, returns the
1370 | * value of the given attribute, or null if attribute is not set.
1371 | *
1372 | * @method attribute
1373 | * @param {String} attr attribute to set
1374 | * @param {String} [value] value to assign to attribute
1375 | * @return {String|Object/p5.Element} value of attribute, if no value is
1376 | * specified or p5.Element
1377 | * @example
1378 | *
1379 | * var myDiv = createDiv("I like pandas.");
1380 | * myDiv.attribute("align", "center");
1381 | *
1382 | */
1383 | p5.Element.prototype.attribute = function(attr, value) {
1384 | if (typeof value === 'undefined') {
1385 | return this.elt.getAttribute(attr);
1386 | } else {
1387 | this.elt.setAttribute(attr, value);
1388 | return this;
1389 | }
1390 | };
1391 |
1392 |
1393 | /**
1394 | *
1395 | * Removes an attribute on the specified element.
1396 | *
1397 | * @method removeAttribute
1398 | * @param {String} attr attribute to remove
1399 | * @return {Object/p5.Element}
1400 | *
1401 | * @example
1402 | *
1403 | * var button;
1404 | * var checkbox;
1405 | *
1406 | * function setup() {
1407 | * checkbox = createCheckbox('enable', true);
1408 | * checkbox.changed(enableButton);
1409 | * button = createButton('button');
1410 | * button.position(10, 10);
1411 | * }
1412 | *
1413 | * function enableButton() {
1414 | * if( this.checked() ) {
1415 | * // Re-enable the button
1416 | * button.removeAttribute('disabled');
1417 | * } else {
1418 | * // Disable the button
1419 | * button.attribute('disabled','');
1420 | * }
1421 | * }
1422 | *
1423 | */
1424 | p5.Element.prototype.removeAttribute = function(attr) {
1425 | this.elt.removeAttribute(attr);
1426 | return this;
1427 | };
1428 |
1429 |
1430 | /**
1431 | * Either returns the value of the element if no arguments
1432 | * given, or sets the value of the element.
1433 | *
1434 | * @method value
1435 | * @param {String|Number} [value]
1436 | * @return {String|Object/p5.Element} value of element if no value is specified or p5.Element
1437 | * @example
1438 | *
1439 | * // gets the value
1440 | * var inp;
1441 | * function setup() {
1442 | * inp = createInput('');
1443 | * }
1444 | *
1445 | * function mousePressed() {
1446 | * print(inp.value());
1447 | * }
1448 | *
1449 | *
1450 | * // sets the value
1451 | * var inp;
1452 | * function setup() {
1453 | * inp = createInput('myValue');
1454 | * }
1455 | *
1456 | * function mousePressed() {
1457 | * inp.value("myValue");
1458 | * }
1459 | *
1460 | */
1461 | p5.Element.prototype.value = function() {
1462 | if (arguments.length > 0) {
1463 | this.elt.value = arguments[0];
1464 | return this;
1465 | } else {
1466 | if (this.elt.type === 'range') {
1467 | return parseFloat(this.elt.value);
1468 | }
1469 | else return this.elt.value;
1470 | }
1471 | };
1472 |
1473 | /**
1474 | *
1475 | * Shows the current element. Essentially, setting display:block for the style.
1476 | *
1477 | * @method show
1478 | * @return {Object/p5.Element}
1479 | * @example
1480 | *
1481 | * var div = createDiv('div');
1482 | * div.style("display", "none");
1483 | * div.show(); // turns display to block
1484 | *
1485 | */
1486 | p5.Element.prototype.show = function() {
1487 | this.elt.style.display = 'block';
1488 | return this;
1489 | };
1490 |
1491 | /**
1492 | * Hides the current element. Essentially, setting display:none for the style.
1493 | *
1494 | * @method hide
1495 | * @return {Object/p5.Element}
1496 | * @example
1497 | *
1498 | * var div = createDiv('this is a div');
1499 | * div.hide();
1500 | *
1501 | */
1502 | p5.Element.prototype.hide = function() {
1503 | this.elt.style.display = 'none';
1504 | return this;
1505 | };
1506 |
1507 | /**
1508 | *
1509 | * Sets the width and height of the element. AUTO can be used to
1510 | * only adjust one dimension. If no arguments given returns the width and height
1511 | * of the element in an object.
1512 | *
1513 | * @method size
1514 | * @param {Number} [w] width of the element
1515 | * @param {Number} [h] height of the element
1516 | * @return {Object/p5.Element}
1517 | * @example
1518 | *
1519 | * var div = createDiv('this is a div');
1520 | * div.size(100, 100);
1521 | *
1522 | */
1523 | p5.Element.prototype.size = function(w, h) {
1524 | if (arguments.length === 0){
1525 | return { 'width' : this.elt.offsetWidth , 'height' : this.elt.offsetHeight };
1526 | }else{
1527 | var aW = w;
1528 | var aH = h;
1529 | var AUTO = p5.prototype.AUTO;
1530 | if (aW !== AUTO || aH !== AUTO) {
1531 | if (aW === AUTO) {
1532 | aW = h * this.width / this.height;
1533 | } else if (aH === AUTO) {
1534 | aH = w * this.height / this.width;
1535 | }
1536 | // set diff for cnv vs normal div
1537 | if (this.elt instanceof HTMLCanvasElement) {
1538 | var j = {};
1539 | var k = this.elt.getContext('2d');
1540 | for (var prop in k) {
1541 | j[prop] = k[prop];
1542 | }
1543 | this.elt.setAttribute('width', aW * this._pInst._pixelDensity);
1544 | this.elt.setAttribute('height', aH * this._pInst._pixelDensity);
1545 | this.elt.setAttribute('style', 'width:' + aW + 'px; height:' + aH + 'px');
1546 | this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity);
1547 | for (var prop in j) {
1548 | this.elt.getContext('2d')[prop] = j[prop];
1549 | }
1550 | } else {
1551 | this.elt.style.width = aW+'px';
1552 | this.elt.style.height = aH+'px';
1553 | this.elt.width = aW;
1554 | this.elt.height = aH;
1555 | this.width = aW;
1556 | this.height = aH;
1557 | }
1558 |
1559 | this.width = this.elt.offsetWidth;
1560 | this.height = this.elt.offsetHeight;
1561 |
1562 | if (this._pInst) { // main canvas associated with p5 instance
1563 | if (this._pInst._curElement.elt === this.elt) {
1564 | this._pInst._setProperty('width', this.elt.offsetWidth);
1565 | this._pInst._setProperty('height', this.elt.offsetHeight);
1566 | }
1567 | }
1568 | }
1569 | return this;
1570 | }
1571 | };
1572 |
1573 | /**
1574 | * Removes the element and deregisters all listeners.
1575 | * @method remove
1576 | * @example
1577 | *
1578 | * var myDiv = createDiv('this is some text');
1579 | * myDiv.remove();
1580 | *
1581 | */
1582 | p5.Element.prototype.remove = function() {
1583 | // deregister events
1584 | for (var ev in this._events) {
1585 | this.elt.removeEventListener(ev, this._events[ev]);
1586 | }
1587 | if (this.elt.parentNode) {
1588 | this.elt.parentNode.removeChild(this.elt);
1589 | }
1590 | delete(this);
1591 | };
1592 |
1593 |
1594 |
1595 | // =============================================================================
1596 | // p5.MediaElement additions
1597 | // =============================================================================
1598 |
1599 |
1600 | /**
1601 | * Extends p5.Element to handle audio and video. In addition to the methods
1602 | * of p5.Element, it also contains methods for controlling media. It is not
1603 | * called directly, but p5.MediaElements are created by calling createVideo,
1604 | * createAudio, and createCapture.
1605 | *
1606 | * @class p5.MediaElement
1607 | * @constructor
1608 | * @param {String} elt DOM node that is wrapped
1609 | * @param {Object} [pInst] pointer to p5 instance
1610 | */
1611 | p5.MediaElement = function(elt, pInst) {
1612 | p5.Element.call(this, elt, pInst);
1613 |
1614 | var self = this;
1615 | this.elt.crossOrigin = 'anonymous';
1616 |
1617 | this._prevTime = 0;
1618 | this._cueIDCounter = 0;
1619 | this._cues = [];
1620 | this._pixelDensity = 1;
1621 |
1622 | /**
1623 | * Path to the media element source.
1624 | *
1625 | * @property src
1626 | * @return {String} src
1627 | */
1628 | Object.defineProperty(self, 'src', {
1629 | get: function() {
1630 | var firstChildSrc = self.elt.children[0].src;
1631 | var srcVal = self.elt.src === window.location.href ? '' : self.elt.src;
1632 | var ret = firstChildSrc === window.location.href ? srcVal : firstChildSrc;
1633 | return ret;
1634 | },
1635 | set: function(newValue) {
1636 | for (var i = 0; i < self.elt.children.length; i++) {
1637 | self.elt.removeChild(self.elt.children[i]);
1638 | }
1639 | var source = document.createElement('source');
1640 | source.src = newValue;
1641 | elt.appendChild(source);
1642 | self.elt.src = newValue;
1643 | },
1644 | });
1645 |
1646 | // private _onended callback, set by the method: onended(callback)
1647 | self._onended = function() {};
1648 | self.elt.onended = function() {
1649 | self._onended(self);
1650 | }
1651 | };
1652 | p5.MediaElement.prototype = Object.create(p5.Element.prototype);
1653 |
1654 |
1655 |
1656 |
1657 | /**
1658 | * Play an HTML5 media element.
1659 | *
1660 | * @method play
1661 | * @return {Object/p5.Element}
1662 | */
1663 | p5.MediaElement.prototype.play = function() {
1664 | if (this.elt.currentTime === this.elt.duration) {
1665 | this.elt.currentTime = 0;
1666 | }
1667 |
1668 | if (this.elt.readyState > 1) {
1669 | this.elt.play();
1670 | } else {
1671 | // in Chrome, playback cannot resume after being stopped and must reload
1672 | this.elt.load();
1673 | this.elt.play();
1674 | }
1675 | return this;
1676 | };
1677 |
1678 | /**
1679 | * Stops an HTML5 media element (sets current time to zero).
1680 | *
1681 | * @method stop
1682 | * @return {Object/p5.Element}
1683 | */
1684 | p5.MediaElement.prototype.stop = function() {
1685 | this.elt.pause();
1686 | this.elt.currentTime = 0;
1687 | return this;
1688 | };
1689 |
1690 | /**
1691 | * Pauses an HTML5 media element.
1692 | *
1693 | * @method pause
1694 | * @return {Object/p5.Element}
1695 | */
1696 | p5.MediaElement.prototype.pause = function() {
1697 | this.elt.pause();
1698 | return this;
1699 | };
1700 |
1701 | /**
1702 | * Set 'loop' to true for an HTML5 media element, and starts playing.
1703 | *
1704 | * @method loop
1705 | * @return {Object/p5.Element}
1706 | */
1707 | p5.MediaElement.prototype.loop = function() {
1708 | this.elt.setAttribute('loop', true);
1709 | this.play();
1710 | return this;
1711 | };
1712 | /**
1713 | * Set 'loop' to false for an HTML5 media element. Element will stop
1714 | * when it reaches the end.
1715 | *
1716 | * @method noLoop
1717 | * @return {Object/p5.Element}
1718 | */
1719 | p5.MediaElement.prototype.noLoop = function() {
1720 | this.elt.setAttribute('loop', false);
1721 | return this;
1722 | };
1723 |
1724 |
1725 | /**
1726 | * Set HTML5 media element to autoplay or not.
1727 | *
1728 | * @method autoplay
1729 | * @param {Boolean} autoplay whether the element should autoplay
1730 | * @return {Object/p5.Element}
1731 | */
1732 | p5.MediaElement.prototype.autoplay = function(val) {
1733 | this.elt.setAttribute('autoplay', val);
1734 | return this;
1735 | };
1736 |
1737 | /**
1738 | * Sets volume for this HTML5 media element. If no argument is given,
1739 | * returns the current volume.
1740 | *
1741 | * @param {Number} [val] volume between 0.0 and 1.0
1742 | * @return {Number|p5.MediaElement} current volume or p5.MediaElement
1743 | * @method volume
1744 | */
1745 | p5.MediaElement.prototype.volume = function(val) {
1746 | if (typeof val === 'undefined') {
1747 | return this.elt.volume;
1748 | } else {
1749 | this.elt.volume = val;
1750 | }
1751 | };
1752 |
1753 | /**
1754 | * If no arguments are given, returns the current playback speed of the
1755 | * element. The speed parameter sets the speed where 2.0 will play the
1756 | * element twice as fast, 0.5 will play at half the speed, and -1 will play
1757 | * the element in normal speed in reverse.(Note that not all browsers support
1758 | * backward playback and even if they do, playback might not be smooth.)
1759 | *
1760 | * @method speed
1761 | * @param {Number} [speed] speed multiplier for element playback
1762 | * @return {Number|Object/p5.MediaElement} current playback speed or p5.MediaElement
1763 | */
1764 | p5.MediaElement.prototype.speed = function(val) {
1765 | if (typeof val === 'undefined') {
1766 | return this.elt.playbackRate;
1767 | } else {
1768 | this.elt.playbackRate = val;
1769 | }
1770 | };
1771 |
1772 | /**
1773 | * If no arguments are given, returns the current time of the element.
1774 | * If an argument is given the current time of the element is set to it.
1775 | *
1776 | * @method time
1777 | * @param {Number} [time] time to jump to (in seconds)
1778 | * @return {Number|Object/p5.MediaElement} current time (in seconds)
1779 | * or p5.MediaElement
1780 | */
1781 | p5.MediaElement.prototype.time = function(val) {
1782 | if (typeof val === 'undefined') {
1783 | return this.elt.currentTime;
1784 | } else {
1785 | this.elt.currentTime = val;
1786 | }
1787 | };
1788 |
1789 | /**
1790 | * Returns the duration of the HTML5 media element.
1791 | *
1792 | * @method duration
1793 | * @return {Number} duration
1794 | */
1795 | p5.MediaElement.prototype.duration = function() {
1796 | return this.elt.duration;
1797 | };
1798 | p5.MediaElement.prototype.pixels = [];
1799 | p5.MediaElement.prototype.loadPixels = function() {
1800 | if (!this.canvas) {
1801 | this.canvas = document.createElement('canvas');
1802 | this.drawingContext = this.canvas.getContext('2d');
1803 | }
1804 | if (this.loadedmetadata) { // wait for metadata for w/h
1805 | if (this.canvas.width !== this.elt.width) {
1806 | this.canvas.width = this.elt.width;
1807 | this.canvas.height = this.elt.height;
1808 | this.width = this.canvas.width;
1809 | this.height = this.canvas.height;
1810 | }
1811 | this.drawingContext.drawImage(this.elt, 0, 0, this.canvas.width, this.canvas.height);
1812 | p5.Renderer2D.prototype.loadPixels.call(this);
1813 | }
1814 | return this;
1815 | }
1816 | p5.MediaElement.prototype.updatePixels = function(x, y, w, h){
1817 | if (this.loadedmetadata) { // wait for metadata
1818 | p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
1819 | }
1820 | return this;
1821 | }
1822 | p5.MediaElement.prototype.get = function(x, y, w, h){
1823 | if (this.loadedmetadata) { // wait for metadata
1824 | return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
1825 | } else if (!x) {
1826 | return new p5.Image(1, 1);
1827 | } else {
1828 | return [0, 0, 0, 255];
1829 | }
1830 | };
1831 | p5.MediaElement.prototype.set = function(x, y, imgOrCol){
1832 | if (this.loadedmetadata) { // wait for metadata
1833 | p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
1834 | }
1835 | };
1836 | p5.MediaElement.prototype.copy = function(){
1837 | p5.Renderer2D.prototype.copy.apply(this, arguments);
1838 | };
1839 | p5.MediaElement.prototype.mask = function(){
1840 | this.loadPixels();
1841 | p5.Image.prototype.mask.apply(this, arguments);
1842 | };
1843 | /**
1844 | * Schedule an event to be called when the audio or video
1845 | * element reaches the end. If the element is looping,
1846 | * this will not be called. The element is passed in
1847 | * as the argument to the onended callback.
1848 | *
1849 | * @method onended
1850 | * @param {Function} callback function to call when the
1851 | * soundfile has ended. The
1852 | * media element will be passed
1853 | * in as the argument to the
1854 | * callback.
1855 | * @return {Object/p5.MediaElement}
1856 | * @example
1857 | *
1858 | * function setup() {
1859 | * audioEl = createAudio('assets/beat.mp3');
1860 | * audioEl.showControls(true);
1861 | * audioEl.onended(sayDone);
1862 | * }
1863 | *
1864 | * function sayDone(elt) {
1865 | * alert('done playing ' + elt.src );
1866 | * }
1867 | *
1868 | */
1869 | p5.MediaElement.prototype.onended = function(callback) {
1870 | this._onended = callback;
1871 | return this;
1872 | };
1873 |
1874 |
1875 | /*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
1876 |
1877 | /**
1878 | * Send the audio output of this element to a specified audioNode or
1879 | * p5.sound object. If no element is provided, connects to p5's master
1880 | * output. That connection is established when this method is first called.
1881 | * All connections are removed by the .disconnect() method.
1882 | *
1883 | * This method is meant to be used with the p5.sound.js addon library.
1884 | *
1885 | * @method connect
1886 | * @param {AudioNode|p5.sound object} audioNode AudioNode from the Web Audio API,
1887 | * or an object from the p5.sound library
1888 | */
1889 | p5.MediaElement.prototype.connect = function(obj) {
1890 | var audioContext, masterOutput;
1891 |
1892 | // if p5.sound exists, same audio context
1893 | if (typeof p5.prototype.getAudioContext === 'function') {
1894 | audioContext = p5.prototype.getAudioContext();
1895 | masterOutput = p5.soundOut.input;
1896 | } else {
1897 | try {
1898 | audioContext = obj.context;
1899 | masterOutput = audioContext.destination
1900 | } catch(e) {
1901 | throw 'connect() is meant to be used with Web Audio API or p5.sound.js'
1902 | }
1903 | }
1904 |
1905 | // create a Web Audio MediaElementAudioSourceNode if none already exists
1906 | if (!this.audioSourceNode) {
1907 | this.audioSourceNode = audioContext.createMediaElementSource(this.elt);
1908 |
1909 | // connect to master output when this method is first called
1910 | this.audioSourceNode.connect(masterOutput);
1911 | }
1912 |
1913 | // connect to object if provided
1914 | if (obj) {
1915 | if (obj.input) {
1916 | this.audioSourceNode.connect(obj.input);
1917 | } else {
1918 | this.audioSourceNode.connect(obj);
1919 | }
1920 | }
1921 |
1922 | // otherwise connect to master output of p5.sound / AudioContext
1923 | else {
1924 | this.audioSourceNode.connect(masterOutput);
1925 | }
1926 |
1927 | };
1928 |
1929 | /**
1930 | * Disconnect all Web Audio routing, including to master output.
1931 | * This is useful if you want to re-route the output through
1932 | * audio effects, for example.
1933 | *
1934 | * @method disconnect
1935 | */
1936 | p5.MediaElement.prototype.disconnect = function() {
1937 | if (this.audioSourceNode) {
1938 | this.audioSourceNode.disconnect();
1939 | } else {
1940 | throw 'nothing to disconnect';
1941 | }
1942 | };
1943 |
1944 |
1945 | /*** SHOW / HIDE CONTROLS ***/
1946 |
1947 | /**
1948 | * Show the default MediaElement controls, as determined by the web browser.
1949 | *
1950 | * @method showControls
1951 | */
1952 | p5.MediaElement.prototype.showControls = function() {
1953 | // must set style for the element to show on the page
1954 | this.elt.style['text-align'] = 'inherit';
1955 | this.elt.controls = true;
1956 | };
1957 |
1958 | /**
1959 | * Hide the default mediaElement controls.
1960 | *
1961 | * @method hideControls
1962 | */
1963 | p5.MediaElement.prototype.hideControls = function() {
1964 | this.elt.controls = false;
1965 | };
1966 |
1967 | /*** SCHEDULE EVENTS ***/
1968 |
1969 | /**
1970 | * Schedule events to trigger every time a MediaElement
1971 | * (audio/video) reaches a playback cue point.
1972 | *
1973 | * Accepts a callback function, a time (in seconds) at which to trigger
1974 | * the callback, and an optional parameter for the callback.
1975 | *
1976 | * Time will be passed as the first parameter to the callback function,
1977 | * and param will be the second parameter.
1978 | *
1979 | *
1980 | * @method addCue
1981 | * @param {Number} time Time in seconds, relative to this media
1982 | * element's playback. For example, to trigger
1983 | * an event every time playback reaches two
1984 | * seconds, pass in the number 2. This will be
1985 | * passed as the first parameter to
1986 | * the callback function.
1987 | * @param {Function} callback Name of a function that will be
1988 | * called at the given time. The callback will
1989 | * receive time and (optionally) param as its
1990 | * two parameters.
1991 | * @param {Object} [value] An object to be passed as the
1992 | * second parameter to the
1993 | * callback function.
1994 | * @return {Number} id ID of this cue,
1995 | * useful for removeCue(id)
1996 | * @example
1997 | *
1998 | * function setup() {
1999 | * background(255,255,255);
2000 | *
2001 | * audioEl = createAudio('assets/beat.mp3');
2002 | * audioEl.showControls();
2003 | *
2004 | * // schedule three calls to changeBackground
2005 | * audioEl.addCue(0.5, changeBackground, color(255,0,0) );
2006 | * audioEl.addCue(1.0, changeBackground, color(0,255,0) );
2007 | * audioEl.addCue(2.5, changeBackground, color(0,0,255) );
2008 | * audioEl.addCue(3.0, changeBackground, color(0,255,255) );
2009 | * audioEl.addCue(4.2, changeBackground, color(255,255,0) );
2010 | * audioEl.addCue(5.0, changeBackground, color(255,255,0) );
2011 | * }
2012 | *
2013 | * function changeBackground(val) {
2014 | * background(val);
2015 | * }
2016 | *
2017 | */
2018 | p5.MediaElement.prototype.addCue = function(time, callback, val) {
2019 | var id = this._cueIDCounter++;
2020 |
2021 | var cue = new Cue(callback, time, id, val);
2022 | this._cues.push(cue);
2023 |
2024 | if (!this.elt.ontimeupdate) {
2025 | this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
2026 | }
2027 |
2028 | return id;
2029 | };
2030 |
2031 | /**
2032 | * Remove a callback based on its ID. The ID is returned by the
2033 | * addCue method.
2034 | *
2035 | * @method removeCue
2036 | * @param {Number} id ID of the cue, as returned by addCue
2037 | */
2038 | p5.MediaElement.prototype.removeCue = function(id) {
2039 | for (var i = 0; i < this._cues.length; i++) {
2040 | var cue = this._cues[i];
2041 | if (cue.id === id) {
2042 | this.cues.splice(i, 1);
2043 | }
2044 | }
2045 |
2046 | if (this._cues.length === 0) {
2047 | this.elt.ontimeupdate = null
2048 | }
2049 | };
2050 |
2051 | /**
2052 | * Remove all of the callbacks that had originally been scheduled
2053 | * via the addCue method.
2054 | *
2055 | * @method clearCues
2056 | */
2057 | p5.MediaElement.prototype.clearCues = function() {
2058 | this._cues = [];
2059 | this.elt.ontimeupdate = null;
2060 | };
2061 |
2062 | // private method that checks for cues to be fired if events
2063 | // have been scheduled using addCue(callback, time).
2064 | p5.MediaElement.prototype._onTimeUpdate = function() {
2065 | var playbackTime = this.time();
2066 |
2067 | for (var i = 0 ; i < this._cues.length; i++) {
2068 | var callbackTime = this._cues[i].time;
2069 | var val = this._cues[i].val;
2070 |
2071 |
2072 | if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
2073 |
2074 | // pass the scheduled callbackTime as parameter to the callback
2075 | this._cues[i].callback(val);
2076 | }
2077 |
2078 | }
2079 |
2080 | this._prevTime = playbackTime;
2081 | };
2082 |
2083 |
2084 | // Cue inspired by JavaScript setTimeout, and the
2085 | // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
2086 | var Cue = function(callback, time, id, val) {
2087 | this.callback = callback;
2088 | this.time = time;
2089 | this.id = id;
2090 | this.val = val;
2091 | };
2092 |
2093 | // =============================================================================
2094 | // p5.File
2095 | // =============================================================================
2096 |
2097 |
2098 | /**
2099 | * Base class for a file
2100 | * Using this for createFileInput
2101 | *
2102 | * @class p5.File
2103 | * @constructor
2104 | * @param {File} file File that is wrapped
2105 | * @param {Object} [pInst] pointer to p5 instance
2106 | */
2107 | p5.File = function(file, pInst) {
2108 | /**
2109 | * Underlying File object. All normal File methods can be called on this.
2110 | *
2111 | * @property file
2112 | */
2113 | this.file = file;
2114 |
2115 | this._pInst = pInst;
2116 |
2117 | // Splitting out the file type into two components
2118 | // This makes determining if image or text etc simpler
2119 | var typeList = file.type.split('/');
2120 | /**
2121 | * File type (image, text, etc.)
2122 | *
2123 | * @property type
2124 | */
2125 | this.type = typeList[0];
2126 | /**
2127 | * File subtype (usually the file extension jpg, png, xml, etc.)
2128 | *
2129 | * @property subtype
2130 | */
2131 | this.subtype = typeList[1];
2132 | /**
2133 | * File name
2134 | *
2135 | * @property name
2136 | */
2137 | this.name = file.name;
2138 | /**
2139 | * File size
2140 | *
2141 | * @property size
2142 | */
2143 | this.size = file.size;
2144 |
2145 | /**
2146 | * URL string containing image data.
2147 | *
2148 | * @property data
2149 | */
2150 | this.data = undefined;
2151 | };
2152 |
2153 | }));
2154 |
--------------------------------------------------------------------------------
/03_chain/sketch.js:
--------------------------------------------------------------------------------
1 | // Daniel Shiffman
2 | // Matter.js + p5.js Examples
3 | // This example is based on examples from: http://brm.io/matter-js/
4 |
5 | var Engine = Matter.Engine;
6 | var Render = Matter.Render;
7 | var World = Matter.World;
8 | var Body = Matter.Body;
9 | var Bodies = Matter.Bodies;
10 | var Composite = Matter.Composite;
11 | var Composites = Matter.Composites;
12 | var Constraint = Matter.Constraint;
13 |
14 | var Mouse = Matter.Mouse;
15 | var MouseConstraint = Matter.MouseConstraint;
16 |
17 | var engine;
18 | var world;
19 | var bodies;
20 |
21 | var canvas;
22 | var constraint;
23 |
24 | var mouseConstraint;
25 |
26 | function setup() {
27 | canvas = createCanvas(800, 600);
28 |
29 |
30 | // create an engine
31 | engine = Engine.create();
32 | world = engine.world;
33 |
34 | var mouse = Mouse.create(canvas.elt);
35 | var mouseParams = {
36 | mouse: mouse,
37 | constraint: {
38 | stiffness: 0.1,
39 | }
40 | }
41 | mouseConstraint = MouseConstraint.create(engine, mouseParams);
42 | mouseConstraint.mouse.pixelRatio = pixelDensity();
43 | World.add(world, mouseConstraint);
44 |
45 |
46 | var group = Body.nextGroup(true);
47 |
48 |
49 | // Make a single rectangle
50 | function makeRect(x, y) {
51 | var params = {
52 | collisionFilter: {
53 | group: group
54 | }
55 | }
56 | var body = Bodies.rectangle(x, y, 50, 20, params);
57 | // adding properties that I can pick up later
58 | body.w = 50;
59 | body.h = 20;
60 | return body;
61 | }
62 |
63 | // Create a stack of rectangles
64 | // x, y, columns, rows, column gap, row gap
65 | var ropeA = Composites.stack(width / 2, 100, 1, 9, 0, 25, makeRect);
66 | bodies = ropeA.bodies;
67 |
68 | // Connect them as a chain
69 | var params = {
70 | stiffness: 0.8,
71 | length: 2
72 | }
73 | Composites.chain(ropeA, 0.5, 0, -0.5, 0, params);
74 |
75 | var params = {
76 | bodyB: ropeA.bodies[0],
77 | pointB: {
78 | x: -25,
79 | y: 0
80 | },
81 | pointA: {
82 | x: width / 2,
83 | y: 100
84 | },
85 | stiffness: 0.5
86 | };
87 |
88 | constraint = Constraint.create(params);
89 | Composite.add(ropeA, constraint);
90 |
91 | // add all of the bodies to the world
92 | World.add(world, ropeA);
93 |
94 | // run the engine
95 | Engine.run(engine);
96 | }
97 |
98 | function draw() {
99 | background(51);
100 | stroke(255);
101 | strokeWeight(1);
102 | fill(255, 50);
103 | for (var i = 0; i < bodies.length; i++) {
104 | var circle = bodies[i];
105 | var pos = circle.position;
106 | var r = circle.circleRadius;
107 | var angle = circle.angle;
108 | push();
109 | translate(pos.x, pos.y);
110 | rectMode(CENTER);
111 | rotate(angle);
112 | rect(0, 0, 50, 20);
113 | line(0, 0, 25, 0);
114 | pop();
115 | }
116 |
117 | var a = constraint.pointA;
118 | var b = constraint.pointB;
119 | var pos = constraint.bodyB.position;
120 | stroke(255);
121 | fill(255);
122 | line(a.x, a.y, pos.x + b.x, pos.y + b.y);
123 | ellipse(a.x, a.y, 4, 4);
124 | ellipse(pos.x + b.x, pos.y + b.y, 4, 4);
125 |
126 | var a = mouseConstraint.constraint.pointA;
127 | var b = mouseConstraint.constraint.pointB;
128 | var bodyB = mouseConstraint.constraint.bodyB;
129 | if (bodyB) {
130 | stroke(255);
131 | line(a.x, a.y, b.x + bodyB.position.x, b.y + bodyB.position.y);
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # p5-matter
2 |
3 | Examples combining p5.js and matter.js. These examples accompany the [Coding Train video tutorials with matter.js physics](https://www.youtube.com/playlist?list=PLRqwX-V7Uu6bLh3T_4wtrmVHOrOEM1ig_) youtube videos.
4 |
--------------------------------------------------------------------------------