Text area in svg shapes - svg

Created a text area in svg using joint js. However, I am not able to enter any text in the text area. How to make the text area editable?
Code:
var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({
el: $('#myholder'),
width: 1500,
height: 700,
model: graph
});
// Create a custom element.
// ------------------------
joint.shapes.html = {};
joint.shapes.html.Element = joint.shapes.basic.Generic.extend(_.extend({}, joint.shapes.basic.PortsModelInterface, {
markup: '<g class="rotatable"><g class="scalable"><rect/></g><g class="inPorts"/><g class="outPorts"/></g>',
portMarkup: '<g class="port<%=1%>"><circle/></g>',
defaults: joint.util.deepSupplement({
type: 'html.Element',
size: {width: 100, height: 80},
inPorts: [],
outPorts: [],
attrs: {
'.': {magnet: false},
rect: {
stroke: 'none', 'fill-opacity': 0, width: 150, height: 250,
},
circle: {
r: 6, //circle radius
magnet: true,
stroke: 'black'
},
'.inPorts circle': {fill: 'green', magnet: 'passive', type: 'input'},
'.outPorts circle': {fill: 'red', type: 'output'}
}
}, joint.shapes.basic.Generic.prototype.defaults),
getPortAttrs: function (portName, index, total, selector, type) {
var attrs = {};
var portClass = 'port' + index;
var portSelector = selector + '>.' + portClass;
var portCircleSelector = portSelector + '>circle';
attrs[portCircleSelector] = {port: {id: portName || _.uniqueId(type), type: type}};
attrs[portSelector] = {ref: 'rect', 'ref-y': (index + 1) * (10 / total)};
if (selector === '.outPorts') {
attrs[portSelector]['ref-dx'] = 0;
}
return attrs;
}
}));
// Create a custom view for that element that displays an HTML div above it.
// -------------------------------------------------------------------------
joint.shapes.html.ElementView = joint.dia.ElementView.extend({
template: [
'<div class="html-element">',
'<button class="delete">x</button>',
'<span id="lbl" value="Please write here"></span>',
'<textarea id="txt" type="text" value="Please write here"></textarea>',
'</div>'
].join(''),
initialize: function () {
_.bindAll(this, 'updateBox');
joint.dia.ElementView.prototype.initialize.apply(this, arguments);
this.$box = $(_.template(this.template)());
// Prevent paper from handling pointerdown.
this.$box.find('input,select').on('mousedown click', function (evt) {
evt.stopPropagation();
});
// This is an example of reacting on the input change and storing the input data in the cell model.
this.$box.find('textarea').on('change', _.bind(function (evt) {
this.model.set('textarea', $(evt.target).val());
}, this));
this.$box.find('.delete').on('click', _.bind(this.model.remove, this.model));
// Update the box position whenever the underlying model changes.
this.model.on('change', this.updateBox, this);
// Remove the box when the model gets removed from the graph.
this.model.on('remove', this.removeBox, this);
this.updateBox();
this.listenTo(this.model, 'process:ports', this.update);
joint.dia.ElementView.prototype.initialize.apply(this, arguments);
},
render: function () {
joint.dia.ElementView.prototype.render.apply(this, arguments);
this.paper.$el.prepend(this.$box);
// this.paper.$el.mousemove(this.onMouseMove.bind(this)), this.paper.$el.mouseup(this.onMouseUp.bind(this));
this.updateBox();
return this;
},
renderPorts: function () {
var $inPorts = this.$('.inPorts').empty();
var $outPorts = this.$('.outPorts').empty();
var portTemplate = _.template(this.model.portMarkup);
_.each(_.filter(this.model.ports, function (p) {
return p.type === 'in'
}), function (port, index) {
$inPorts.append(V(portTemplate({id: index, port: port})).node);
});
_.each(_.filter(this.model.ports, function (p) {
return p.type === 'out'
}), function (port, index) {
$outPorts.append(V(portTemplate({id: index, port: port})).node);
});
},
update: function () {
// First render ports so that `attrs` can be applied to those newly created DOM elements
// in `ElementView.prototype.update()`.
this.renderPorts();
joint.dia.ElementView.prototype.update.apply(this, arguments);
},
updateBox: function () {
// Set the position and dimension of the box so that it covers the JointJS element.
var bbox = this.model.getBBox();
// Example of updating the HTML with a data stored in the cell model.
// paper.on('blank:pointerdown', function(evt, x, y) { this.$box.find('textarea').toBack(); });
this.$box.find('span').text(this.model.get('textarea'));
this.model.on('cell:pointerclick', function (evt, x, y) {
this.$box.find('textarea').toFront();
});
this.$box.css({width: bbox.width, height: bbox.height, left: bbox.x, top: bbox.y, transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)'});
},
removeBox: function (evt) {
this.$box.remove();
}
});
// Create JointJS elements and add them to the graph as usual.
// -----------------------------------------------------------
var el1 = new joint.shapes.html.Element({
position: {x: 600, y: 250},
size: {width: 170, height: 100},
inPorts: ['in'],
outPorts: ['out'],
textarea: 'Start writing'
});
var el2 = new joint.shapes.html.Element({
position: {x: 600, y: 400},
size: {width: 170, height: 100},
inPorts: ['in'],
outPorts: ['out'],
textarea: 'Start writing'
});
graph.addCells([el1, el2]);
Also is it possible to scale the svg shape based on the text inside text area?

I assume that you are using the CSS stylesheet from the JointJS HTML tutorial (http://jointjs.com/tutorial/html-elements).
Note that .html-element has pointer-events set to none. With that being set all events are propagated to the (JointJS) SVG Element under the HTML Element. The paper therefore knows what element was interacted with and e.g. can start dragging it.
.html-element {
pointer-events: none;
}
I suggest to create an exception for the TextArea by adding the following CSS rule.
.html-element textarea {
pointer-events: all;
}

Related

How to run mediapipe facemesh on a ES6 node.js environment alike react

I am trying to run this HTML example https://codepen.io/mediapipe/details/KKgVaPJ from https://google.github.io/mediapipe/solutions/face_mesh#javascript-solution-api in a create react application. I have already done:
npm install of all the facemesh mediapipe packages.
Already replaced the jsdelivr tags with node imports and I got the definitions and functions.
Replaced the video element with react-cam
I don't know how to replace this jsdelivr, maybe is affecting:
const faceMesh = new FaceMesh({
locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/#mediapipe/face_mesh/${file}`;
}
});
So the question is:
Why the facemesh is not showing? Is there any example of what I am trying to do?
This is my App.js code (sorry for the debugugging scaffolding):
import './App.css';
import React, { useState, useEffect } from "react";
import Webcam from "react-webcam";
import { Camera, CameraOptions } from '#mediapipe/camera_utils'
import {
FaceMesh,
FACEMESH_TESSELATION,
FACEMESH_RIGHT_EYE,
FACEMESH_LEFT_EYE,
FACEMESH_RIGHT_EYEBROW,
FACEMESH_LEFT_EYEBROW,
FACEMESH_FACE_OVAL,
FACEMESH_LIPS
} from '#mediapipe/face_mesh'
import { drawConnectors } from '#mediapipe/drawing_utils'
const videoConstraints = {
width: 1280,
height: 720,
facingMode: "user"
};
function App() {
const webcamRef = React.useRef(null);
const canvasReference = React.useRef(null);
const [cameraReady, setCameraReady] = useState(false);
let canvasCtx
let camera
const videoElement = document.getElementsByClassName('input_video')[0];
// const canvasElement = document.getElementsByClassName('output_canvas')[0];
const canvasElement = document.createElement('canvas');
console.log('canvasElement', canvasElement)
console.log('canvasCtx', canvasCtx)
useEffect(() => {
camera = new Camera(webcamRef.current, {
onFrame: async () => {
console.log('{send}',await faceMesh.send({ image: webcamRef.current.video }));
},
width: 1280,
height: 720
});
canvasCtx = canvasReference.current.getContext('2d');
camera.start();
console.log('canvasReference', canvasReference)
}, [cameraReady]);
function onResults(results) {
console.log('results')
canvasCtx.save();
canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);
canvasCtx.drawImage(
results.image, 0, 0, canvasElement.width, canvasElement.height);
if (results.multiFaceLandmarks) {
for (const landmarks of results.multiFaceLandmarks) {
drawConnectors(canvasCtx, landmarks, FACEMESH_TESSELATION, { color: '#C0C0C070', lineWidth: 1 });
drawConnectors(canvasCtx, landmarks, FACEMESH_RIGHT_EYE, { color: '#FF3030' });
drawConnectors(canvasCtx, landmarks, FACEMESH_RIGHT_EYEBROW, { color: '#FF3030' });
drawConnectors(canvasCtx, landmarks, FACEMESH_LEFT_EYE, { color: '#30FF30' });
drawConnectors(canvasCtx, landmarks, FACEMESH_LEFT_EYEBROW, { color: '#30FF30' });
drawConnectors(canvasCtx, landmarks, FACEMESH_FACE_OVAL, { color: '#E0E0E0' });
drawConnectors(canvasCtx, landmarks, FACEMESH_LIPS, { color: '#E0E0E0' });
}
}
canvasCtx.restore();
}
const faceMesh = new FaceMesh({
locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/#mediapipe/face_mesh/${file}`;
}
});
faceMesh.setOptions({
selfieMode: true,
maxNumFaces: 1,
minDetectionConfidence: 0.5,
minTrackingConfidence: 0.5
});
faceMesh.onResults(onResults);
// const camera = new Camera(webcamRef.current, {
// onFrame: async () => {
// await faceMesh.send({ image: videoElement });
// },
// width: 1280,
// height: 720
// });
// camera.start();
return (
<div className="App">
<Webcam
audio={false}
height={720}
ref={webcamRef}
screenshotFormat="image/jpeg"
width={1280}
videoConstraints={videoConstraints}
onUserMedia={() => {
console.log('webcamRef.current', webcamRef.current);
// navigator.mediaDevices
// .getUserMedia({ video: true })
// .then(stream => webcamRef.current.srcObject = stream)
// .catch(console.log);
setCameraReady(true)
}}
/>
<canvas
ref={canvasReference}
style={{
position: "absolute",
marginLeft: "auto",
marginRight: "auto",
left: 0,
right: 0,
textAlign: "center",
zindex: 9,
width: 1280,
height: 720,
}}
/>
</div >
);
}
export default App;
You don't have to replace the jsdelivr, that piece of code is fine; also I think you need to reorder your code a little bit:
You should put the faceMesh initialization inside the useEffect, with [] as parameter; therefore, the algorithm will start when the page is rendered for the first time
Also, you don't need to get videoElement and canvasElement with doc.*, because you already have some refs defined
An example of code:
useEffect(() => {
const faceMesh = new FaceDetection({
locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/#mediapipe/face_detection/${file}`;
},
});
faceMesh.setOptions({
maxNumFaces: 1,
minDetectionConfidence: 0.5,
minTrackingConfidence: 0.5,
});
faceMesh.onResults(onResults);
if (
typeof webcamRef.current !== "undefined" &&
webcamRef.current !== null
) {
camera = new Camera(webcamRef.current.video, {
onFrame: async () => {
await faceMesh.send({ image: webcamRef.current.video });
},
width: 1280,
height: 720,
});
camera.start();
}
}, []);
Finally, in the onResults callback I would suggest printing first the results, just to check if the Mediapipe implementation is working fine. And don't forget to set the canvas size before drawing something.
function onResults(results){
console.log(results)
canvasCtx = canvasReference.current.getContext('2d')
canvas.width = webcamRef.current.video.videoWidth;
canvas.height = webcamRef.current.video.videoHeight;;
...
}
Good luck! :)

Code for a platform moving back and Forth

I'am trying to make a game where the character can jump on a platform that's moving Horizontally, once the platform reaches a specific point it comes back and it repeats. However I have had some trouble finding the correct way to write the code for this I tried using setVelocityX() is looks like this
var movingPlatform = {
moveRight : function(){
platforms.setVelocityX(100)
},
moveleft : function(){
platforms.setVelocityX(-100);
}
}
var move = true;
if(move = true){
movingPlatform.moveRight()
}
if(move = false){
movingPlatform.moveleft();
}
if(platforms.x <= platformMinX){
move = true;
}
if(platforms.x >= platformMaxX){
move = false;
}
all this did was when the platform reaches the 'platformMaxX' point it just moved back and forth in that area so 'movingPlatform.moveRight()' is still being called
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600, loader: {
baseURL: 'https://raw.githubusercontent.com/nazimboudeffa/assets/master/',
crossOrigin: 'anonymous'
},
scene: {
preload: preload,
create: create,
update: update
},
physics: {
default: 'arcade'
}
};
var game = new Phaser.Game(config);
var dude;
var alien1, alien2;
var direction = 1
function preload ()
{
this.load.image('dude', 'sprites/phaser-dude.png');
this.load.image('alien1', 'sprites/phaser-alien.png');
this.load.image('alien2', 'sprites/alien2.png');
}
function create ()
{ dude = this.physics.add.sprite(300, 100, 'dude');
alien1 = this.physics.add.sprite(400, 100, 'alien1');
alien1.body.immovable = true;
alien2 = this.physics.add.sprite(100, 100, 'alien2');
alien2.body.immovable = true;
}
function update ()
{
this.physics.add.collider(dude, alien1, flipX, null, this);
this.physics.add.collider(dude, alien2, flipX, null, this);
dude.setVelocityX(direction * 100)
}
function flipX ()
{
direction = - direction
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.18.1/dist/phaser.min.js"></script>
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600, loader: {
baseURL: 'https://raw.githubusercontent.com/nazimboudeffa/assets/master/',
crossOrigin: 'anonymous'
},
scene: {
preload: preload,
create: create
},
physics: {
default: 'arcade'
}
};
var game = new Phaser.Game(config);
function preload ()
{
this.load.image('dude', 'sprites/phaser-dude.png');
}
function create ()
{
this.dude = this.physics.add.sprite(100, 100, 'dude');
const tween = this.tweens.add({
targets: this.dude,
x: 300,
ease: 'Power0',
duration: 3000,
flipX: true,
yoyo: true,
repeat: -1,
});
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.19.0/dist/phaser.js"></script>

Phaser 3 - How to create smooth zooming effect

How can I make a zooming effect instead of instant zooming?
Hi there, i'm currently developing a game, which will zoom in when the condition is true. To do that, i followed this link.
https://rexrainbow.github.io/phaser3-rex-notes/docs/site/camera/
camera = this.cameras.main;
if (condition) {
camera.setZoom(3);
camera.zoom = 3;
}
It works, but there's no transition/animation of the zooming. It just simply zoomed.
How can I make a zooming effect instead of instant zooming?
You can play with the pan call and replace 'Power2' by :
Elastic
Sine.easeInOut
Or leave it blank
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600, loader: {
baseURL: 'https://raw.githubusercontent.com/nazimboudeffa/assets/master/',
crossOrigin: 'anonymous'
},
scene: {
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
function preload ()
{
this.load.image('map', 'pics/hyrule.png');
}
function create ()
{
this.cameras.main.setBounds(0, 0, 1024, 1024);
this.add.image(0, 0, 'map').setOrigin(0);
this.cameras.main.setZoom(1);
this.cameras.main.centerOn(0, 0);
this.input.on('pointerdown', function () {
var cam = this.cameras.main;
cam.pan(500, 500, 2000, 'Power2');
cam.zoomTo(4, 3000);
}, this);
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.17.0/dist/phaser.min.js"></script>

How do I fire fabric.js 'object: moving' event when object is being dragged on a canvas?

I am currently developing an application using the fabric.js library where I draw a rectangle object on a fabric.js canvas which has an image background set on it. I can draw the rectangle but then when I click on the rectangle to move it, the 'object:moving' event does not get fired at all and a new rectangle gets drawn while the current selected one is being moved. Is there a way to stop drawing a new rectangle object while moving the current selected one? Below is my JavaScript code.$scope.c is the canvas variable in scope.
$scope.rect = new fabric.Rect({
top: $scope.origY,
left: $scope.origX,
width: 0,
height: 0,
fill: 'gold',
stroke: 'red',
strokewidth: 4.5,
opacity: 0.5,
name: 'UserArea_'
});
$scope.c.add($scope.rect);
});
$scope.c.on('mouse:move', function(o) {
if (!$scope.isDown) {
return
};
var pointer = $scope.c.getPointer(o.e);
if ($scope.origX > pointer.x) {
$scope.rect.set({
left: Math.abs(pointer.x)
});
}
if ($scope.origY > pointer.y) {
$scope.rect.set({
top: Math.abs(pointer.y)
});
}
$scope.rect.set({
width: Math.abs($scope.origX - pointer.x)
});
$scope.rect.set({
height: Math.abs($scope.origY - pointer.y)
});
$scope.rect.setCoords();
});
$scope.c.on('mouse:up', function(o) {
$scope.isDown = false;
});
$scope.c.on('object:selected', function(e) {
var activeObj = $scope.c.getActiveObject(); {
console.log(activeObj.width);
}
});
$scope.c.on('object: moving', function(e) {
console.log('object is moving');
});
You have a space inside event name 'object: moving', should be:
$scope.c.on('object:moving', function(e) {
console.log('object is moving');
});

How can I modify the value and font of a text class in fabric.js dynamically?

I am working with fabric.js and I have used a fabric.Text class like this:
var text = new fabric.Text('hello world', {
fontSize: 30,
left:190,
});
var circle = new fabric.Circle({
radius: 100,
fill: '#eef',
scaleY: 0.5
});
var group = new fabric.Group([ text, circle ], {
left: 150,
top: 100,
angle: -10
});
canvas.add(rect1, rect2, rect3, circle, triangle,group);
canvas.on({
'object:moving': onChange,
'object:scaling': onChange,
'object:rotating': onChange,
});
function onChange(options) {
options.target.setCoords();
canvas.forEachObject(function(obj) {
if (obj === options.target)
return;
obj.setOpacity(options.target.intersectsWithObject(obj) ? 0.5 : 1);
});
}
The main problem is that I can not modify the value of the text of the text class dynamically. How I can modify it?
Try with text.set({ text: 'New Text' });
Regards.

Resources