Add new line in es6 inside a doughnut chart - reactjs

i'm using chartjs and i've added the plugin for adding text inside this chart. My problem is that when i define text like this:
text: ${sectorsCounter}\ Sectors it didnt show the Sectors under the sectorsCounter. My desired output is like this:
The plugin i use for adding text is:
Chart.pluginService.register({
afterUpdate(chart) {
let helpers;
let centerConfig;
let globalConfig;
let ctx;
let fontStyle;
let fontFamily;
let fontSize;
if (chart.config.options.elements.center) {
helpers = Chart.helpers;
centerConfig = chart.config.options.elements.center;
globalConfig = Chart.defaults.global;
ctx = chart.chart.ctx;
fontStyle = helpers.getValueOrDefault(
centerConfig.fontStyle, globalConfig.defaultFontStyle
);
fontFamily = helpers.getValueOrDefault(
centerConfig.fontFamily, globalConfig.defaultFontFamily
);
if (centerConfig.fontSize) {
fontSize = centerConfig.fontSize;
} else {
ctx.save();
fontSize = helpers.getValueOrDefault(centerConfig.minFontSize, 1);
ctx.restore();
}
const newChart = chart;
newChart.center = {
font: helpers.fontString(fontSize, fontStyle, fontFamily),
fillStyle: helpers.getValueOrDefault(
centerConfig.fontColor, globalConfig.defaultFontColor
)
};
}
},
afterDraw(chart) {
if (chart.center) {
const centerConfig = chart.config.options.elements.center;
const ctx = chart.chart.ctx;
ctx.save();
ctx.font = chart.center.font;
ctx.fillStyle = chart.center.fillStyle;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const centerX = (chart.chartArea.left + chart.chartArea.right) / 2;
const centerY = (chart.chartArea.top + chart.chartArea.bottom) / 2;
ctx.fillText(centerConfig.text, centerX, centerY);
ctx.restore();
}
},
});
And when i the call of Doughnut chart is:
<Doughnut
data={sectorsData}
width={250}
height={250}
options={{
legend: {
display: false
},
maintainAspectRatio: false,
responsive: true,
cutoutPercentage: 75,
elements: {
center: {
text: `${sectorsCounter}\ Sectors`,
fontColor: '#000000',
fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
fontStyle: 'normal',
minFontSize: 25,
maxFontSize: 25,
}
},

Well, I see you haven't found your desired solution yet. SO, here's the solution...
add the following code after const centerY = (chart.chartArea.top + chart.chartArea.bottom)/2
in your plugin
let helpers = Chart.helpers;
let fontSize = helpers.getValueOrDefault(centerConfig.minFontSize, 1);
let text = centerConfig.text.split('\ ');
ctx.fillText(text[0], centerX, centerY - fontSize / 2);
ctx.fillText(text[1], centerX, centerY + fontSize / 2);

See Escape notation:
let stringTemplateA = `-a
a-`;
let stringTemplateB = `-b\r\nb-`;
console.log(stringTemplateA);
console.log(stringTemplateB);

Related

The app gets slower with every change in props [duplicate]

This question already has an answer here:
React won't draw anything in the canvas in useEffect
(1 answer)
Closed 16 days ago.
I use Matter js for physics and canvas for rendering 100 items, after every change in props the app gets slower and slower. At the same time, subsequent downloads with the same props occur instantly, i.e. past images and elements remained and were not deleted, which loads the application.
enter image description here
"Top" dropdown menu changes props for bubbles component, code:
const Bubbles = ({ coins }) => {
var winwidth = window.innerWidth;
var winheight = window.innerHeight - 80;
const [physics, setPhysics] = useState({
frictionAir: 0,
friction: 0.2,
frictionStatic: 0.2,
restitution: 0.1,
inertia: Infinity,
density: 0.8,
});
useEffect(() => {
setSize(coins);
// create an engine
const engine = Engine.create();
const world = engine.world;
// create a renderer
const render = Render.create({
element: document.querySelector(".bubbles"),
engine: engine,
options: {
width: winwidth,
height: winheight,
showAngleIndicator: false,
background: "transparent",
wireframeBackground: "transparent",
wireframes: false,
showDebug: false,
},
});
const bubbles = [];
// create a canvas
const canvas = document.createElement("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.querySelector(".bubbles").appendChild(canvas);
const ctx = canvas.getContext("2d");
// create 100 random bubbles
for (let i = 0; i < coins.length; i++) {
const x = Math.random() * window.innerWidth;
const y = Math.random() * window.innerHeight;
const bubble = Bodies.circle(x, y, coins[i].size / 2, {
...physics,
render: {
fillStyle: "transparent",
},
});
Composite.add(engine.world, bubble);
// store the bubble and its content data in an object
bubbles.push({
bubble,
content: {
radius: coins[i].radius,
text: `${i + 1}`,
},
});
}
// update the canvas on each engine update
Matter.Events.on(engine, "afterUpdate", function () {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < bubbles.length; i++) {
const bubble = coins[i];
const pos = bubbles[i].bubble.position;
const size = bubble.size;
const text = bubble.symbol;
const priceChange = bubble.price_change_percentage_24h.toFixed(1);
const image = new Image();
image.src = bubble.image;
if (priceChange > 0) {
ctx.fillStyle = "rgba(5, 222, 5, 0.15)";
ctx.strokeStyle = "rgba(5, 222, 5, 0.6)";
} else if (priceChange < 0) {
ctx.fillStyle = "rgba(166, 53, 53, 0.15)";
ctx.strokeStyle = "rgba(166, 53, 53, 0.8)";
} else {
ctx.fillStyle = "rgba(255, 255, 89, 0.069)";
ctx.strokeStyle = "rgba(187, 231, 11, 0.443)";
}
ctx.beginPath();
ctx.arc(pos.x, pos.y, size / 2, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.drawImage(
image,
pos.x - size / 5,
pos.y - size / 2.5,
size / 2.5,
size / 2.5
);
ctx.font = `${size / 6}px Acme`;
ctx.fillStyle = "white";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(text, pos.x, pos.y + size / 6.5);
ctx.font = `${size / 6.5}px Acme`;
ctx.fillStyle = "white";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(priceChange + "%", pos.x, pos.y + size / 3);
}
});
// fit the render viewport to the scene
window.addEventListener("onload", () => {
Render.lookAt(render, {
min: { x: 0, y: 0 },
max: { x: 100000, y: 10000 },
});
});
// run the engine
Runner.run(engine);
// run the renderer
Render.run(render);
return () => {
// stop the engine and renderer
Engine.clear(engine);
Render.stop(render);
Runner.stop(engine);
// remove the canvas from the DOM
canvas.remove();
};
}, [coins]);
return <></>;
};
export default Bubbles;
after each update of the coins props, I deleted the canvas and stopped the engine, but still the speed worsened
return () => {
// stop the engine and renderer
Engine.clear(engine);
Render.stop(render);
Runner.stop(engine);
// remove the canvas from the DOM
canvas.remove();
};
I want to either stop re-creating the engine and the matter js world, canvas with images.
Only without window.location.reload(), because I need to save props like "Top" and "Earth, Trampoline", etc.
I solved the problem. I didn't properly clear the js canvas value and also didn't set events.off when unmounting the component.
Here is the solution to my problem
return () => {
//set Matter Js events off. drawCanvas - afterUpdate event function
Events.off(engine, 'afterUpdate', drawCanvas);
Render.stop(render);
Composite.clear(engine.world);
Runner.stop(engine)
Engine.clear(engine);
render.canvas.remove();
render.canvas = null;
render.context = null;
// remove the canvas from the DOM
canvas.remove();
};

How to add local image of project folder in canvas?

I am trying to open a project folder image in the canvas without uploading it in a file input. But it is not showing there and I am using react-konva to add the image.
Here is the code where I am trying to upload the image:
const img = new window.Image();
img.src = require('../../../assets/images/71.png');
img.onload = function () {
const img_width = this.width;
console.log('the image', require('../../../assets/images/71.png'));
const img_height = this.height;
const max = img_width > img_height ? 100 : 100;
const ratio = (img_width > img_height ? (img_width / max) : (img_height / max))
setOriginalDim({ width: img_width, height: img_height });
setDimensions({ width: img_width/ratio, height: img_height/ratio });
const theImg = new Konva.Image({
image: this,
x: 0,
y: 0,
width: img_width/ratio,
height: img_height/ratio,
name: 'background_image',
draggable: true
});
if (layerRef.current != null) {
console.log('image', theImg);
layerRef.current.add(theImg);
layerRef.current.draw();
}
};
And my react component is this:
<Stage
width={dimensions.width}
height={dimensions.height}
onMouseDown={handleStageMouseDown}
style={{ backgroundColor: '#fff', boxShadow: '0 0 6px 0 #666' }}
ref={stageRef}
id="certificate_canvas"
>
<Layer
ref={layerRef}
/>
</Stage>
The onload part is not executing also the image is not loading into the canvas. How do I add the project folder image without uploading it
instead of this
img.src = require('../../../assets/images/71.png');
use
img.src = '../../../assets/images/71.png';
you dont need the require function

How do I get the camera to focus on the object and keep it in center focus using Babylonjs?

What I really want is to put the mesh on the object and have the camera focus on that mesh. I think they do this with the lookAt function, but I don't know how to use it correctly.
I got help from this page : https://www.babylonjs-playground.com/#1CSVHO#12
I tried some function demos.
setCamera_Mesh = () => {
let { currentWidth, currentDepth, rowCount } = this.currentConfig;
let sphere = Mesh.CreateSphere("sphere", 1, this.scene);
let referenceBox = Mesh.CreateBox("referenceBox", { width: 1, height: 1, depth: 1, updatable: true });
sphere.scaling = new Vector3(0.1, 0.1, 0.1);
sphere.position = this.scene.cameras[0].position;
sphere.parent = this.scene.cameras[0];
this.referenceBox && this.referenceBox.dispose()
referenceBox.position = new Vector3(0, 0, 0.08);
referenceBox.enableEdgesRendering();
referenceBox.edgesWidth = 1;
referenceBox.edgesColor = new Color4(0, 0, 1, 0.05);
referenceBox.visibility = 0.5;
referenceBox.scaling = new Vector3(currentDepth / 40, rowCount / 3, currentWidth / 100);
this.referenceBox = referenceBox;
sphere.lookAt(referenceBox.position);
}
I had to use setParent () instead of .parent where I added the camera as parent.
When I edit the code as below, it worked correctly.
setCameraToMesh = () => {
let { currentWidth, currentDepth, rowCount } = this.currentConfig;
let sphere = MeshBuilder.CreateSphere("referenceSphere", this.scene);
let referenceBox = MeshBuilder.CreateBox("referenceBox", { width: 0.1, height: 0.1, depth: 0.1, updatable: true });
sphere.scaling = new Vector3(0.1, 0.1, 0.1);
sphere.position = this.scene.cameras[0].position;
sphere.setParent(this.scene.cameras[0]);
this.referenceBox && this.referenceBox.dispose()
referenceBox.position = new Vector3(0, rowCount / 500, 0.08);
referenceBox.enableEdgesRendering();
referenceBox.edgesWidth = 1;
referenceBox.edgesColor = new Color4(0, 0, 1, 0.05);
referenceBox.visibility = 0.05;
referenceBox.scaling = new Vector3(currentDepth / 40, rowCount / 3, currentWidth / 100);
this.referenceBox = referenceBox;
sphere.lookAt(referenceBox.absolutePosition);
this.scene.cameras[0].focusOn([referenceBox], true);
}

how to show speedometer on Doughnut chart in chart.js using reactjs

I've created a half doughnut chart and wanted to show like a speedometer, but couldn't able to make it. I have gone through this link I want to display my chart Either as similar to the above link or as per this snippet
Here is my code:
import React, { Component } from "react";
import { Doughnut } from "react-chartjs-2";
import Data from "./Data.json";
const data = {
// labels: ["Red", "Green", "Yellow"],
datasets: [
{
data: [500, 500, 500],
backgroundColor: ["red", "#FFCE56", "lightgreen"],
hoverBackgroundColor: ["red", "#FFCE56", "lightgreen"]
}
],
options: {
rotation: 1 * Math.PI,
circumference: 1 * Math.PI,
legend: {
display: false
},
tooltip: {
enabled: false
},
cutoutPercentage: 70
}
};
export default class DonutChart extends Component {
render() {
return (
<div>
<h2>Sample</h2>
<Doughnut height="100px" data={data} options={data.options} />
</div>
);
}
}
Here is the sample code
Can anyone help me in this query to achieve in this format?
Base on the code sample from Konstantin S., you could register a plugin globally inside the componentDidMount() method and implement an afterDraw function as follows:
componentDidMount() {
Chart.pluginService.register({
afterDraw: chart => {
var needleValue = chart.chart.config.data.datasets[0].needleValue;
var dataTotal = chart.chart.config.data.datasets[0].data.reduce((a, b) => a + b, 0);
var angle = Math.PI + (1 / dataTotal * needleValue * Math.PI);
var ctx = chart.chart.ctx;
var cw = chart.chart.canvas.offsetWidth;
var ch = chart.chart.canvas.offsetHeight;
var cx = cw / 2;
var cy = ch - 6;
ctx.translate(cx, cy);
ctx.rotate(angle);
ctx.beginPath();
ctx.moveTo(0, -3);
ctx.lineTo(ch - 10, 0);
ctx.lineTo(0, 3);
ctx.fillStyle = 'rgb(0, 0, 0)';
ctx.fill();
ctx.rotate(-angle);
ctx.translate(-cx, -cy);
ctx.beginPath();
ctx.arc(cx, cy, 5, 0, Math.PI * 2);
ctx.fill();
}
});
}
This expects the property needleValue to be defined inside the dataset.
datasets: [{
data: [500, 500, 500],
needleValue: 580,
...
}],
Please have a look at your amended CodeSandbox

Raycaster display a Render

I use Threejs and I would like to create a function to display a descriptive card (Render() of Satllite.js) when I click on a Sphere. (Satellite.js)
/********** Imports **********/
import React, { PureComponent } from 'react';
import * as THREE from 'three';
import satData from '../data/sat.json';
export class Satellites extends PureComponent {
constructor(props) {
super(props)
this.state = { open: true }
this.x = {};
this.mouse = {};
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
}
onDocumentMouseDown = event => {
event.preventDefault()
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
// find intersections
this.raycaster.setFromCamera(this.mouse, this.props.camera)
this.intersects = this.raycaster.intersectObjects(
this.scene.className("card"),
false
)
if (this.intersects.length > 0) {
if (this.intersects[0].object.callback)
this.intersects[0].object.callback()
this.intersects[0].className(".card")
this.particle = new THREE.Sprite(this.particleMaterial)
this.particle.position.copy(this.intersects[0].point)
this.particle.scale.x = this.particle.scale.y = 16
this.props.scene.add(this.particle)
}
}
componentDidMount() {
// Satellite Sphere
// this.geometry = new THREE.SphereGeometry( this.props.data.r, 32, 32 );
this.geometry = new THREE.SphereGeometry(10, 32, 32)
this.material = new THREE.MeshBasicMaterial({ color: 0xffff00 })
this.sphere = new THREE.Mesh(this.geometry, this.material)
this.sphere.callback = function() {
console.log('Toto!')
}
this.sphere.position.set(50, 50, 50)
this.props.scene.add(this.sphere)
document.addEventListener('mousedown', this.onDocumentMouseDown, true)
}
// componentDidUpdate() {
// // update satelite pos.
// const radius = 10;
// const scale = radius * 1;
// this.sphere.scale.x = scale;
// this.sphere.scale.y = scale;
// this.sphere.scale.z = scale;
// }
componentWillUnmount() {
document.removeEventListener('mousedown', this.onDocumentMouseDown);
}
render() {
return (
<div>
{satData.map((satDetail, index) => {
return <div key={index} className="card">
<h2>{satDetail.satName.toUpperCase()}</h2>
<div className="cardImg" >
<img src={satDetail.satImg} alt={satDetail.satAlt} />
</div>
<div>
<p>Altitude : <span>{satDetail.alt}</span> km</p>
<p>Longitude : <span>{satDetail.long}</span> °</p>
<p>Latitude : <span>{satDetail.lat}</span> °</p>
</div>
<button onClick={this.closeModal}>Fermer</button>
</div>
})}
</div>
)
}
}
More details here
I got the code of a tutorial that show white squares but I would like to display my div ".card" which is in the "Render"
What is the method ?
Thanks for help !
This answer involves many topics combined, basically you need:
Raycast the mouse click and find the object intersection,
Get info by picked object,
Display it if object was selected, or hide if nothing was picked by mouse.
Test it here: http://jsfiddle.net/mmalex/9k4qbL8s/
let cardShown = false;
function showCard(userText) {
var divElement = $("#card");
if (divElement) {
if (!cardShown) {
divElement.css({
display: "block",
opacity: 0,
height: "0px"
});
}
divElement.text("Object color: " + userText);
if (!cardShown) {
setTimeout(function() {
divElement.css({
opacity: 1,
height: "16px"
});
}, 25);
}
cardShown = true;
}
}
function hideCard() {
var divElement = $("#card");
if (divElement) {
divElement.css({
height: "0px",
opacity: 0
});
cardShown = false;
}
}
var scene = new THREE.Scene();
var raycaster = new THREE.Raycaster();
//create some camera
camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = 5;
camera.position.y = 5;
camera.position.z = 5;
camera.lookAt(0, 0, 0);
var controls = new THREE.OrbitControls(camera);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color(0x595959));
document.body.appendChild(renderer.domElement);
// white spotlight shining from the side, casting a shadow
var spotLight = new THREE.SpotLight(0xffffff, 2.5, 25, Math.PI / 6);
spotLight.position.set(4, 10, 7);
scene.add(spotLight);
// collect objects for raycasting,
// for better performance don't raytrace all scene
var clickableObjects = [];
var colors = new RayysWebColors();
for (let k = 0; k < 12; k++) {
var size = 0.5;
var geometry = new THREE.BoxGeometry(size, 0.2, size);
var randomColor = colors.pickRandom();
var material = new THREE.MeshPhongMaterial({
color: randomColor.hex,
transparent: true,
opacity: 0.75
});
var cube = new THREE.Mesh(geometry, material);
cube.userData.userText = randomColor.name;
cube.applyMatrix(new THREE.Matrix4().makeTranslation(k % 3, 0, Math.floor(k / 3) - 1));
scene.add(cube);
clickableObjects.push(cube);
}
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
// this will be 2D coordinates of the current mouse position, [0,0] is middle of the screen.
var mouse = new THREE.Vector2();
var clickedObj; // this objects is hovered at the moment
// Following two functions will convert mouse coordinates
// from screen to three.js system (where [0,0] is in the middle of the screen)
function updateMouseCoords(event, coordsObj) {
coordsObj.x = ((event.clientX - renderer.domElement.offsetLeft + 0.5) / window.innerWidth) * 2 - 1;
coordsObj.y = -((event.clientY - renderer.domElement.offsetTop + 0.5) / window.innerHeight) * 2 + 1;
}
function onMouseUp(event) {
updateMouseCoords(event, mouse);
latestMouseProjection = undefined;
clickedObj = undefined;
raycaster.setFromCamera(mouse, camera); {
var intersects = raycaster.intersectObjects(clickableObjects);
if (intersects.length > 0) {
latestMouseProjection = intersects[0].point;
clickedObj = intersects[0].object;
showCard(clickedObj.userData.userText);
} else {
clickedObj = undefined;
hideCard();
}
}
}
window.addEventListener('mouseup', onMouseUp, false);
animate();

Resources