I have run the React samples for Three.js, but can't seem to work out how to add an object (mesh, geometry) outside componentDidMount.
I've tried to add new objects, dispose of previous objects, update bufferGeometry
componentDidMount() {
const width = this.mount.clientWidth
const height = this.mount.clientHeight
//ADD SCENE
this.scene = new THREE.Scene()
//ADD CAMERA
this.camera = new THREE.PerspectiveCamera(
75,
width / height,
0.1,
1000
)
this.camera.position.z = 4
//ADD RENDERER
this.renderer = new THREE.WebGLRenderer({ antialias: true })
this.renderer.setClearColor('#000000')
this.renderer.setSize(width, height)
this.mount.appendChild(this.renderer.domElement)
//ADD CUBE
const geometry = new THREE.BoxGeometry(12, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: '#FFFF81' })
this.cube = new THREE.Mesh(geometry, material)
this.scene.add(this.cube)
this.start()
}
---------------------------------------------
addIT(){
const geometry3 = new THREE.BoxBufferGeometry(2,3, 1)
const material3 = new THREE.MeshBasicMaterial({ color: '#00FF81' })
this.cube3 = new THREE.Mesh(geometry3, material3)
this.scene.add(this.cube3)
this.start()
}
What I'd like to get to is for a react event to be able to add or remove an object from the scene.
At the moment, I get nothing.
Any thoughts would be appreciated.
I eventually worked out what was happening. Cube3 wasn't being added and an error was being thrown. I placed a stop render in addIt added the object and then started the render again.
Related
Hope you all are doing best in you life!
I've newly intraction with 3d work. I'm making the 3d virtual showroom you can say 3d commerce page,where i need to put 3d Models. I want to add functionality of changing models color (limited colors) by users. like this: [https://codesandbox.io/s/v1fgk].(I'm using three js not react three fiber.)
THREE JS Version: "three": "^0.135.0",
The problem is that:
1) When i change the color of the mesh the details of the model are deleted.what can i do for this?
2) I face alot of lighting issues during this work.finally I'm using HDRI envornment instead of lighting in my models.
am i doing best?
Actual Model:
On Change Color:
Why my models's details are going to delete?
On Changing Color i'm doing this:
// const loader = new THREE.TextureLoader();
// const aoTexture = loader.load('textures/BottleModel/Model2/Material.002_Mixed_AO.png')
// const displacementTexture = loader.load('textures/BottleModel/Model2/base_Height.png')
// const metalicTexture = loader.load('textures/BottleModel/Model2/Material.002_Metallic.png')
// const roughnessTexture = loader.load('textures/BottleModel/Model2/Material.002_Roughness.png')
const BottleMesh=model.current.children.find((item)=>item.name==="cup_base")
BottleMesh.children.map((item)=>{
if(item.name==="Cylinder"){
item.material = new THREE.MeshStandardMaterial({
color: code,
// aoMap:aoTexture,
// metalnessMap : metalicTexture,
// displacementMap:displacementTexture,
// roughnessMap:roughnessTexture
})
}
})
CREATING MODEL AND ADDING ENVORNMENT:
export const createModel = (model, scene, path, setLoading,renderer) => { //model is a react ref, remember that
setLoading(true)
const dracoLoader = new DRACOLoader();
// // Specify path to a folder containing WASM/JS decoding libraries.
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');
let loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
let generator = new THREE.PMREMGenerator(renderer);
new RGBELoader().setPath('/textures/hdri/') .load('dancing_hall_4k.hdr', function (texture) {
// let envmap =generator.fromEquirectangular(texture)
texture.mapping = THREE.EquirectangularReflectionMapping;
// scene.background = texture;
scene.environment = texture;
});
loader.load(path, function (gltf) {
model.current = gltf.scene;
model.current.position.x=0
model.current.position.y=-3
scene.add(model.current);
setLoading(false);
}, undefined, function (error) {
console.error(error)
});
}
I am using the SVGRenderer to draw the total value in the center of donut chart shown below:
The code to do this is shown below (please see CodeSandbox here)
export const PieChart = ({ title, totalLabel, pieSize, pieInnerSize, data }: PieChartProps) => {
const chartRef = useRef(null);
const [chartOptions, setChartOptions] = useState(initialOptions);
useEffect(() => {
// set options from props
setChartOptions({...});
// compute total
const total = data.reduce((accumulator, currentValue) => accumulator + currentValue.y, 0);
// render total
const totalElement = chart.renderer.text(total, 0, 0).add();
const totalElementBox = totalElement.getBBox();
// Place total
totalElement.translate(
chart.plotLeft + (chart.plotWidth - totalElementBox.width) / 2,
chart.plotTop + chart.plotHeight / 2
);
...
}, [title, totalLabel, pieSize, pieInnerSize, data]);
return (
<HighchartsReact
highcharts={Highcharts}
containerProps={{ style: { width: '100%', height: '100%' } }}
options={chartOptions}
ref={chartRef}
/>
);
};
However this approach has two issues:
When chart is resized, the total stays where it is - so it is no longer centered inside the pie.
When the chart data is changed (using the form), the new total is drawn over the existing one.
How do I solve these issues? With React, I expect the chart to be fully re-rendered when it is resized or when the props are changed. However, with Highcharts React, the chart seems to keep internal state which is not overwritten with new props.
I can suggest two options for this case:
Use the events.render callback, destroy and render a new label after each redraw:
Demo: https://jsfiddle.net/BlackLabel/ps97bxkg/
Use the events.render callback to trasnlate those labels after each redraw:
Demo: https://jsfiddle.net/BlackLabel/6kwag80z/
Render callback triggers after each chart redraw, so it is fully useful in this case - more information
API: https://api.highcharts.com/highcharts/chart.events.render
I'm not sure if this helps, but I placed items inside of highcharts using their svgrenderer functionality (labels, in particular, but you can also use the text version of svgrenderer), and was able to make them responsive
I was able to do something by manually changing the x value of my svgRenderer label.
I'm in angular and have a listener for screen resizing:
Also Note that you can change the entire SVGRenderer label with the attr.text property.
this.chart = Highcharts.chart(.....);
// I used a helper method to create the label
this.chart.myLabel = this.labelCreationHelperMethod(this.chart, data);
this.windowEventService.resize$.subscribe(dimensions => {
if(dimensions.x < 500) { //this would be your charts resize breakpoint
// here I was using a specific chart series property to tell where to put my x coordinate,
// you can traverse through the chart object to find a similar number,
// or just use a hardcoded number
this.chart.myLabel.attr({ y: 15, x: this.chart.series[0].points[0].plotX + 20 });
} else {
this.chart.myLabel.attr({ y: 100, x: this.chart.series[0].points[0].plotX + 50 });
}
}
//Returns the label object that we keep a reference to in the chart object.
labelCreationHelperMethod() {
const y = screen.width > 500 ? 100 : 15;
const x = screen.width > 500 ? this.chart.series[0].points[0].plotX + 50 :
this.chart.series[0].points[0].plotX + 20
// your label
const label = `<div style="color: blue"...> My Label Stuff</div>`
return chart.renderer.label(label, x, y, 'callout', offset + chart.plotLeft, chart.plotTop + 80, true)
.attr({
fill: '#e8e8e8',
padding: 15,
r: 5,
zIndex: 6
})
.add();
}
I am using canvas in react and rendering pdf's as images using the canvas.
Now, when I get new data i.e another pdf get's added, then
again have to use the canvases for that.
I am not sure how to fix this error or how to remove the canvas or even clear the canvas before using it again.
Here's the relevant code:
pdfLoop = (item,index) => {
var that = this;
PDFJS.getDocument(item).then(function getPdfHelloWorld(pdf) {
//
// Fetch the first page
console.log('url is : ',item);
pdf.getPage(1).then(function getPageHelloWorld(page) {
var scale = 0.5;
var viewport = page.getViewport(scale);
let cref = 'canvas'+index;
let imgref ='img'+index;
console.log('cref no : ',cref);
console.log('img no : ',imgref);
// Prepare canvas using PDF page dimensions
//
var canvas = that.canvasRefs[cref];
//let imagez = that.imageRefs[imgref];
var context = canvas.getContext('2d');
context.globalcompositeoperation = 'source-over';
// context.fillStyle = "#fff";
//draw on entire canvas
//context.fillRect( 0, 0, canvas.width, canvas.height );
canvas.height = viewport.height;
canvas.width = viewport.width;
//imagez.src = canvas.toDataURL("image/png");
//
// Render PDF page into canvas context
//
//page.render({canvasContext: context, viewport: viewport});
var task = page.render({canvasContext: context, viewport: viewport})
task.promise.then(function(){
//console.log(canvas.toDataURL('image/png'));
let imgItem = {imgref:canvas.toDataURL('image/png'),page:index+1,rotate:0}
let newState = that.state.imgsrc;
newState[index] = imgItem;
//let newState = that.state.imgsrc.concat(imgItem);
that.setState({
imgsrc:newState
});
//imagez.src = canvas.toDataURL('image/png')
});
});
});
}
In case someone stumbles across this, the error message states the following Use different canvas or ensure previous operations were cancelled or completed.
When getting the document, if there already is a document, it has to be destroyed. I.e:
PDFJS.getDocument({ url: pdf_url }).promise
.then((pdf_doc) => {
if (this.pdf_doc) {
this.pdf_doc.destroy();
}
this.pdf_doc = pdf_doc;
this.total_pages = this.pdf_doc.numPages;
})
I have no idea if this is a good solution, but at least it worked for me.
I've had the same exact problem as you, but my solution was a bit different than the answers previously posted.
For me the problem was the fact that I was unnecessarily re-rendering with a state change. Check if you aren't re-rendering the component without properly clearing the canvas, or if you even need to re-render at all (in my case I didn't).
Hopefully this could help
In order to avoid this situation, put your canvas object in a div object as is shown below:
<div id="div_canvas">
<canvas id="cnv"></canvas>
</div>
Then, before to call pdf.js functions, remove the "div_canvas" content and recreate it:
$("#div_canvas").html("");
$("#div_canvas").html("<canvas id='cnv'></canvas>");
I had exactly the same issue when working with PDF.js, the solution to this is,
You will have to clear your context after the render completes.
if (context) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
}
This will make sure that the context is cleared and ready for the next render and the error disappears.
this worked for me.
In some cases, this error will be displayed when the pdf has action buttons such as next/previous or scaling.
In this cases, often you have a function for rendering pdf page such as:
renderPage(num) {
// Using promise to fetch the page
pdfDoc.getPage(num).then(function (page) {
const viewport = page.getViewport({scale: scale});
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
const renderContext = {
canvasContext: ctx,
viewport: viewport
};
page.render(renderContext);
});
}
To fix the error just perform following changes:
Add two variable for controlling conflict and cache waiting page number:
pageRendering = false; // Check conflict
pageNumPending = null; // Cache waiting page number
Use these variables as follow:
renderPage(num) {
if (this.pageRendering) { // Check if other page is rendering
this.pageNumPending = num; // Cache waited page number until previous page rendering completed
} else {
this.pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function (page) {
const viewport = page.getViewport({scale: scale});
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
const renderContext = {
canvasContext: ctx,
viewport: viewport
};
const renderTask = page.render(renderContext);
// Wait for rendering to finish
renderTask.promise.then(function () {
this.pageRendering = false;
if (pageNumPending !== null) {
// Waited page must be rendered
this.renderPage(pageNumPending);
// Must be set to null to prevent infinite loop
this.pageNumPending = null;
}
});
});
One possible cause: React Strict mode renders twice
I'm stuck in a simple animation code which has a no. of components in the container moves from top of the screen to a specific position. I have a bg image set in the UIID "AttempTitle" in the container consisting all the component. The problem is while all the components moves from top, the bg img is not seen until it reaches the specific position and it appears at last. How can I make the bg Image move along with the components.
I got another problem while doing it. I have a bg image in the title container. While the animation is taking place, the green bg image of the title container disappears for a second too. How that happened, I cannot figure out.
I have a video uploaded in the youtube incase you didn't understand the problem have a look at it.
https://youtu.be/6Or26wxnzUY
Code:
setLayout(new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER));
attemptIcon = theme.getImage("attemptIcon.png");
attempt1 = new Label(attemptIcon);
attempt2 = new Label(attemptIcon);
attempt3 = new Label(attemptIcon);
attempt4 = new Label(attemptIcon);
attempt5 = new Label(attemptIcon);
attemptContainer = BoxLayout.encloseX(attempt1, attempt2, attempt3, attempt4, attempt5);
Container titleContainer = new Container(new GridLayout(1));
titleContainer.add(FlowLayout.encloseCenterMiddle(attemptContainer));
add(BorderLayout.NORTH, titleContainer);
attemptContainer.getParent().setUIID("AttempTitle");
attemptContainer.getParent().setPreferredW(screenWidth / 3);
questionAnswerContainer = new Container(new BoxLayout(BoxLayout.Y_AXIS));
Label a = new Label("questin answer");
questionAnswerContainer.add(a);
titleDialog = new Label("Yuppie!");
body1 = new Label("Let’s celebrate");
body2 = new Label("with another");
body3 = new Label("drink");
Button ok = new Button(theme.getImage("playIcon.png"));
ok.addActionListener(e -> {
new Test(sm).show();
});
dialogContainer = (BoxLayout.encloseY(titleDialog, body1, body2, body3, ok));
dialogContainer.getAllStyles().setBgImage(theme.getImage("yuppieDialog.png"));
add(BorderLayout.CENTER, LayeredLayout.encloseIn(questionAnswerContainer, FlowLayout.encloseCenterMiddle(dialogContainer)));
titleDialog.getAllStyles().setMarginTop((dialogContainer.getPreferredW() / 3) + 30);
dialogContainer.getParent().setVisible(false);//using setHidden(true) gives same issue
Runnable r = new Runnable() {
public void run() {
checkIfCorrect(Test.this);
}
};
if (timer == null) {
timer = new UITimer(r);
}
if (timer != null) {
timer.schedule(5000, false, Test.this); //4 seconds
}
revalidate();
checkIfCorrect method:
public void checkIfCorrect(Form f) {
dialogContainer.getParent().setY(-Display.getInstance().getDisplayHeight());
dialogContainer.getParent().setVisible(true);
f.animateHierarchyAndWait(1200);
f.setTransitionInAnimator(null);
}
toolbar code:
Toolbar toolbar = new Toolbar();
form.setToolbar(toolbar);
Container containerTitle = new Container(new BorderLayout());
toolbar.setTitleComponent(LayeredLayout.encloseIn(containerTitle, FlowLayout.encloseCenter(ruslanLogo)))
//there r 4 buttons inside containerTitle container
You should use animateLayout instead of animateHierarchy start by setting the element you want as visible false then show. Then set it's Y to the right location set it to visible and do the animate layout.
The problem is that the background sizing/position of the Container doesn't work properly as a result of the animation.
I am following the approach of
here. The problem is that all the pages are in first canvas. This is because I have only one canvas and I am not sure how I can generate more canvas one after other?
function handlePages(page)
{
var viewport = page.getViewport(canvas.width / page.getViewport(1.0).width);
var ctx = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({ canvasContext: ctx, viewport: viewport });
//Add it to the web page
div.appendChild( canvas);
//Move to next page
currPage++;
if ( $scope.pdfDoc !== null && currPage <= numPages )
{
$scope.pdfDoc.getPage( currPage ).then( handlePages );
}
}
you cut out part of the code from the linked answer that you took this from. notice the document.createElement( "canvas" ). That creates a new canvas for every page.