Why does omitting a 0ms sleep break my css transition? - html

I was trying to implement the FLIP animation, to see if I understood it properly.
In this codepen (pardon the awful code, I was just hacking around), if I comment out the sleep, the smooth transition no longer works. The div changes position abruptly. This is strange because the sleep is for 0ms.
import React, { useRef, useState } from "https://esm.sh/react#18";
import ReactDOM from "https://esm.sh/react-dom#18";
let first = {}
let second = {}
const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const App = () => {
const [start, setStart] = useState(true);
const boxRefCb = async el => {
if (!el) return;
el.style.transition = "";
const x = parseInt(el?.getBoundingClientRect().x, 10);
const y = parseInt(el?.getBoundingClientRect().y, 10);
first = { x: second.x, y: second.y };
second = { x, y };
const dx = first.x - second.x;
const dy = first.y - second.y;
const transStr = `translate(${dx}px, ${dy}px)`;
el.style.transform = transStr;
await sleep(0); // comment me out
el.style.transition = "transform .5s";
el.style.transform = "";
}
return (
<>
<div style={{ display: "flex", gap: "1rem", padding: "3rem"}}>
<div ref={ start ? boxRefCb : null } style={{ visibility: start ? "" : "hidden", width: 100, height: 100, border: "solid 1px grey" }}></div>
<div ref={ !start ? boxRefCb : null } style={{ visibility: !start ? "" : "hidden", width: 100, height: 100, border: "solid 1px grey" }}></div>
</div>
<button style={{ marginLeft: "3rem"}} onClick={() => setStart(start => !start)}>start | {start.toString()}</button>
</>
);
}
ReactDOM.render(<App />,
document.getElementById("root"))
I suspect this is some event loop magic that I don't understand. Could someone shed some light onto this for me please?

What happens is that the browser may have time to recalculate the CSSOM boxes (a.k.a "perform a reflow"), during that sleep. Without it, your transform rule isn't ever really applied.
Indeed, browsers will wait until it's really needed before applying the changes you made, and update the whole page box model, because doing so can be very expensive.
When you do something like
element.style.color = "red";
element.style.color = "yellow";
element.style.color = "green";
all the CSSOM will see is the latest state, "green". The other two are just discarded.
So in your code, when you don't let the event loop actually loop, the transStr value is never seen either.
However, relying on a 0ms setTimeout is a call to issues, there is nothing that does ensure that the styles will get recalculated at that time. Instead, it's better to force a recalc manually. Some DOM methods/properties will do so synchronously. But remember that a reflow can be a very expensive operation, so be sure to use it sporadically, and if you have multiple places in your code in need of this, be sure to concatenate them all so that a single reflow is performed.
const el = document.querySelector(".elem");
const move = () => {
el.style.transition = "";
const transStr = `translate(150px, 0px)`;
el.style.transform = transStr;
const forceReflow = document.querySelector("input").checked;
if (forceReflow) {
el.offsetWidth;
}
el.style.transition = "transform .5s";
el.style.transform = "";
}
document.querySelector("button").onclick = move;
.elem {
width: 100px;
height: 100px;
border: 1px solid grey;
}
.parent {
display: flex;
padding: 3rem;
}
<label><input type=checkbox checked>force reflow</label>
<button>move</button>
<div class=parent>
<div class=elem></div>
</div>
Or with OP's code.

You're approaching this problem with a vanilla JavaScript solution, but React uses a virtual DOM and expects DOM elements to be re-rendered when state has been changed. Therefore, I'd recommend leveraging React state to update the element's XY position within the virtual DOM, but while still using CSS.
Working demo here or the code can be found here:
import { useState, useRef, useLayoutEffect } from "react";
import "./styles.css";
type BoxXYPosition = { x: number; y: number };
export default function App() {
const startBox = useRef<HTMLDivElement | null>(null);
const startBoxPosition = useRef<BoxXYPosition>({ x: 0, y: 0 });
const endBox = useRef<HTMLDivElement | null>(null);
const [boxPosition, setBoxPosition] = useState<BoxXYPosition>({
x: 0,
y: 0
});
const { x, y } = boxPosition;
const hasMoved = Boolean(x || y);
const updatePosition = () => {
if (!endBox.current) return;
const { x: endX, y: endY } = endBox.current.getBoundingClientRect();
const { x: startX, y: startY } = startBoxPosition.current;
// "LAST" - calculate end position
const moveXPosition = endX - startX;
const moveYPosition = endY - startY;
// "INVERT" - recalculate position based upon current x,y coords
setBoxPosition((prevState) => ({
x: prevState.x !== moveXPosition ? moveXPosition : 0,
y: prevState.y !== moveYPosition ? moveYPosition : 0
}));
};
useLayoutEffect(() => {
// "FIRST" - save starting position
if (startBox.current) {
const { x, y } = startBox.current.getBoundingClientRect();
startBoxPosition.current = { x, y };
}
}, []);
// "PLAY" - switch between start and end animation via the x,y state and a style property
return (
<main className="app">
<h1>Transition Between Points</h1>
<div className="container">
<div
ref={startBox}
className="box start-point"
style={{
transform: hasMoved
? `translate(${x}px, ${y}px) rotateZ(360deg)`
: ""
}}
>
{hasMoved ? "End" : "Start"}
</div>
<div className="end-container">
<div ref={endBox} className="box end-point" />
</div>
</div>
<button
type="button"
onClick={updatePosition}
>
Move to {hasMoved ? "Start" : "End"}
</button>
</main>
);
}

Related

I am drawing a real-time chart using React html canvas. The chart flashes when data is updated. is there any solution

If you run the code below, the chart graph will be updated while flashing. Is there any way to get rid of the glitter?
When the data changes, the value of the child changes, so it seems to be my rendering problem, but I don't know what format to change. Any help please
import CanvasLine from '#components/CanvasLine';
import { memo, useContext, useEffect, useState } from 'react';
const CHART_DUMMY_DATA2 = (): Array<number> => {
const date = new Date();
date.setSeconds(date.getSeconds() - 249);
return [...new Array(249)].map((_, _idx) => {
return 0;
});
};
const CanvasLineChartDemo = () => {
const [chartHRArray, setChartHRArray] = useState(CHART_DUMMY_DATA2);
const [chartSP02Array, setChartSP02Array] = useState(CHART_DUMMY_DATA2);
const [chartRESPArray, setChartRESPArray] = useState(CHART_DUMMY_DATA2);
const returnRandomFloat = (min: number, max: number) => {
return (Math.random() * (max - min) + min).toFixed(2);
};
const validate = () => {
setChartHRArray((prevState: any) =>
[...prevState, returnRandomFloat(-100, 100)].slice(1),
);
setChartSP02Array((prevState: any) =>
[...prevState, returnRandomFloat(-100, 100)].slice(1),
);
setChartRESPArray((prevState: any) =>
[...prevState, returnRandomFloat(-100, 100)].slice(1),
);
};
useEffect(() => {
const interval = setInterval(() => {
validate();
}, 100);
return () => {
clearInterval(interval);
};
}, [connectStatus]);
return (
<>
<div
style={{ width: '700px', background: '#000', marginRight: 10 }}
>
vdrDevice {vdrDevice}
<CanvasLine
height={50}
canvasData={chartHRArray}
/>
<CanvasLine
height={50}
canvasData={chartSP02Array}
/>
<CanvasLine
height={50}
canvasData={chartRESPArray}
/>
</div>
</>
);
};
export default memo(CanvasLineChartDemo);
import React, { useCallback, useEffect, useLayoutEffect, useRef } from 'react';
type CanvasType = {
yMin?: number;
yMax?: number;
height: number;
lineColor: string;
canvasData: number[];
};
const CanvasLine = ({
yMax,
yMin,
height,
lineColor,
canvasData,
}: CanvasType) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
let CanvasContext: CanvasRenderingContext2D | null = null;
// Array Max number
const getYMax = (data: number[]) => {
return Math.max.apply(null, data);
};
// Array Min number
const getYMin = (data: number[]) => {
return Math.min.apply(null, data);
};
const calculateY = (
canvasData: number[],
index: number,
height: number,
yMaxNum: number,
yMinNum: number,
) => {
const xAxis = (yMaxNum / (yMaxNum - yMinNum)) * height;
const yScale = -height / (yMaxNum - yMinNum);
const valueRatio = canvasData[index] * yScale + xAxis; // 마이너스영역 및 플러스 영역
return valueRatio;
};
const createCanvas = () => {
const canvas = canvasRef.current;
if (canvas) {
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
canvas.style.width = '100%';
canvas.style.height = `${height}px`;
}
};
useEffect(() => {
createCanvas();
}, []);
useEffect(() => {
const canvas = canvasRef.current;
const yMaxNum = yMax ?? getYMax(canvasData);
const yMinNum = yMin ?? getYMin(canvasData);
if (canvas) {
let xStep = canvas.width / canvasData.length,
xAxis = xStep,
xStart = 0,
yAxis = calculateY(
canvasData,
0,
canvas.height,
yMinNum,
yMaxNum,
);
CanvasContext = canvas.getContext('2d');
if (!canvas.hasChildNodes()) {
CanvasContext?.clearRect(0, 0, canvas.width, canvas.height);
} else {
CanvasContext?.clearRect(0, 0, canvas.width, canvas.height);
}
const canvasDraw = (canvas: HTMLCanvasElement) => {
if (CanvasContext) {
CanvasContext?.clearRect(0, 0, canvas.width, canvas.height);
CanvasContext.lineWidth = 1.5;
CanvasContext.lineJoin = 'miter';
CanvasContext.beginPath();
CanvasContext.moveTo(xStart, yAxis);
CanvasContext.lineCap = 'round';
CanvasContext.strokeStyle = lineColor;
CanvasContext.fillStyle = lineColor;
for (let i = 1; i < canvasData.length; i++) {
yAxis = calculateY(
canvasData,
i,
canvas.height,
yMinNum,
yMaxNum,
);
CanvasContext.lineTo(xAxis, yAxis);
CanvasContext.moveTo(xAxis, yAxis);
xAxis = xAxis + xStep;
}
CanvasContext.stroke();
CanvasContext.fill();
}
};
canvasDraw(canvas);
}
}, [canvasData]);
return (
<>
<canvas id="canvas" style={{ width: '100%' }} ref={canvasRef} />
</>
);
};
export default React.memo(CanvasLine);
CanvasLine.defaultProps = {
height: 50,
lineColor: '#2C82C9',
canvasData: [],
};
You can see that the chart line flickers finely.
[1]: https://i.stack.imgur.com/XvtTd.gif
If you look at the gif, you can find the sparkling phenomenon.
Please suggest a way to solve the problem. Thanks in advance.
`

BoundingClientRectObserver using IntersectionObserver returns empty rects

I'm trying to create an observer that triggers when the bounding rect of an element changes (a BoundingClientRectObserver), and this is what I have so far:
customElements.define(
'bounding-client-rect-observer-helper',
class extends HTMLDivElement {
disconnectedCallback () { this.dispatchEvent(new Event('$disconnected')) }
},
{extends: 'div'}
)
class BoundingClientRectObserver {
#intersectionObserver
#helperElement
#timeoutId
constructor (element, callback, startNow=true) {
if (typeof element == 'string') {
element = document.querySelector(element)
}
this.element = element
this.callback = callback
this.#helperElement = document.createElement(
'div', {is: 'bounding-client-rect-observer-helper'}
)
this.#helperElement.style.position = 'fixed'
this.#helperElement.style.visibility = 'hidden'
this.#intersectionObserver = new IntersectionObserver(
entries => this.#debouncedCallback(entries),
{root: this.element, threshold: [0,1.0]}
)
this.disconnected = this.disconnected.bind(this)
this.running = false
if (startNow) this.start()
}
start () {
if (this.running) return
this.element.append(this.#helperElement)
this.#helperElement.addEventListener('$disconnected', this.disconnected)
this.#intersectionObserver.observe(this.#helperElement)
this.running = true
}
#innerCallback () {
const rect = this.element.getBoundingClientRect()
this.#helperElement.style.width = rect.width + 'px'
this.#helperElement.style.height = rect.height + 'px'
this.#helperElement.style.left = rect.left + 'px'
this.#helperElement.style.top = rect.top + 'px'
this.callback(rect)
}
#debouncedCallback (entries) {
console.log(entries)
clearTimeout(this.#timeoutId)
this.#timeoutId = setTimeout(() => this.#innerCallback(), 200)
}
disconnected () {
if (this.running) {this.element.append(this.#helperElement)}
}
stop () {
if (!this.running) return
this.#intersectionObserver.unobserve(this.#helperElement)
this.#helperElement.removeEventListener('$disconnected', this.disconnected)
this.#helperElement.remove()
this.running = false
}
}
I'm adding a helper element to the root element (the one I want to check if its bounding client rect changes) with the same bounding client rect, since in the IntersectionObserver docs it says that the element to observe should be a descendant of the root element. So in theory, everytime the size or position of the root element changes, the callback should be called, but this is not happening, and I suspect it's because bounding client rects in entries (I'm printing the entries in #debouncedCallback function) are empty. can you see where I got it wrong?

How to check if the model out of the viewer container?

I made an info card and this card will disappear if the viewer is rotated until the model is not visible. I use isNodevisible but it always returns true.
updateInfoCard() {
if (this.infoCard && this.posModel) {
const pos = this.viewer.worldToClient(this.posModel);
console.log(pos);
this.infoCard.style.left = `${Math.floor(
50 + pos.x - this.infoCard.offsetWidth / 2
)}px`;
this.infoCard.style.top = `${Math.floor(
50 + pos.y - this.infoCard.offsetWidth / 2
)}px`;
const id = this.infoCard.dataset.id;
console.log(this.viewer.isNodeVisible(id));
this.infoCard.style.display = this.viewer.isNodeVisible(id)
? "block"
: "none";
}
}
If I understand your question correctly, you'll probably want to do an intersection test between the camera's frustum and the models's bounding box. That can be done like so:
viewer.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, function () {
if (!viewer.model) {
return;
}
const camera = viewer.getCamera();
const matrix = new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
const frustum = new THREE.Frustum().setFromMatrix(matrix);
const bbox = viewer.model.getBoundingBox();
console.log('Model in the view?', frustum.intersectsBox(bbox));
});
And if you only want to check the visibility of a specific element (based on its dbID) of your model, you can compute its bounding box like so:
function objectBounds(model, dbId) {
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
const objectBounds = new THREE.Box3();
tree.enumNodeFragments(dbId, function (fragId) {
const fragBounds = new THREE.Box3();
frags.getWorldBounds(fragId, fragBounds);
objectBounds.union(fragBounds);
}, true);
return objectBounds;
}
The function isNodeVisible returns the visibility status of your node in the scene. If you do something like this.viewer.hide(id, model) your function will return false.
If I well understood what you want to achieve, you want to hide an info card when the associated object is occluded by others objects, so we can't see it from our point of view ?
So I think what you need is to check for occlusion. You can take a look at the checkOcclusion function of this point cloud markup extension made by Philippe Leefsma.
To check for node occlusion, you basically need to raycast from your point of view to the node that you want to check. If you hit something and it's your node, there is no occlusion. If it's not the same node, it's mean that something occlude your node.
checkOcclusion (markup) {
const clientPoint = this.viewer.worldToClient(
markup.point)
const offset = $(this.viewer.container).offset()
const rayCaster = this.pointToRaycaster(
this.viewer.impl.canvas,
this.viewer.impl.camera, {
x: clientPoint.x + offset.left,
y: clientPoint.y + offset.top
})
const hitTest = this.viewer.model.rayIntersect(
rayCaster, true, this.dbIds)
if (hitTest) {
if (hitTest.fragId === markup.fragId) {
const offset = {
x: hitTest.point.x - markup.point.x,
y: hitTest.point.y - markup.point.y,
z: hitTest.point.z - markup.point.z
}
const dist = Math.sqrt(
offset.x * offset.x +
offset.y * offset.y +
offset.z * offset.z)
if (this.options.logOcclusionDist) {
console.log(dist)
}
if (dist < this.options.occlusionDist) {
return false
}
}
return true
}
}

Issue with using HTML for Floating button in react and not material UI

Working on a requirement that needs the usage of floating button to hide and show a menu, implementing this using material UI functions as needed, but if i change this to a button with icon using normal html and css doesn't work as required.
Expected behavior - on click of hide button present on the menu the menu section is hidden when a floating button is shown up, this button is used to flow around the screen on click and mouse drag and can be released freely and when clicked again the menu is again shown,
Where as if i change this to a normal html and css button the behavior is a bubble that appears with drag of the button and also the until i click back i wont be able to release the arrow from the button until clicked.
const [position, setPosition] = useState({ x: 75, y: 75 });
//Ref are needed instead of states so they can be used in handle move
const vector = useRef({ x: 0, y: 0 });
const clicked = useRef(false);
const oldPosition = useRef(position);
useEffect(() => {
const cachedPos = localStorage.getItem('OpenMenuIconPosition');
if (cachedPos) {
const newPos = JSON.parse(cachedPos);
const clampNewPos = {
x: clamp(newPos.x, 0, window.innerWidth - 60),
y: clamp(newPos.y, 0, window.innerHeight - 60)
};
setPosition(clampNewPos);
oldPosition.current = clampNewPos;
}
}, []);
const handleMouseDown = e => {
e.stopPropagation();
clicked.current = true;
vector.current = {
x: e.clientX - position.x,
y: e.clientY - position.y
};
window.addEventListener('mousemove', handleMove);
};
const handleMove = e => {
e.stopPropagation();
if (clicked.current === true) {
setPosition({
x: clamp(e.clientX - vector.current.x, 0, window.innerWidth - 60),
y: clamp(e.clientY - vector.current.y, 0, window.innerHeight - 60)
});
}
};
const handleMouseUp = e => {
e.stopPropagation();
clicked.current = false;
if (Math.abs(oldPosition.current.x - position.x) < 5 && Math.abs(oldPosition.current.y - position.y) < 5) showMenu();
oldPosition.current = position;
window.removeEventListener('mousemove', handleMove);
localStorage.setItem('OpenMenuIconPosition', JSON.stringify(position));
};
const handleTouchDown = e => {
e.stopPropagation();
clicked.current = false;
vector.current = {
x: e.changedTouches[0].clientX - position.x,
y: e.changedTouches[0].clientY - position.y
};
window.addEventListener('touchmove', handleTouchMove);
};
const handleTouchMove = e => {
e.stopPropagation();
if (clicked.current === true) {
setPosition({
x: clamp(e.changedTouches[0].clientX - vector.current.x, 0, window.innerWidth - 60),
y: clamp(e.changedTouches[0].clientY - vector.current.y, 0, window.innerHeight - 60)
});
}
};
const handleTouchUp = e => {
e.stopPropagation();
clicked.current = false;
if (Math.abs(oldPosition.current.x - position.x) < 5 && Math.abs(oldPosition.current.y - position.y) < 5) showMenu();
oldPosition.current = position;
window.removeEventListener('touchmove', handleTouchMove);
localStorage.setItem('OpenMenuIconPosition', JSON.stringify(position));
};
const [overButton, setOverButton] = useState(false);
const mouseMoveListener = e => {
if (e.clientX < 200 && e.clientY < 200) {
setOverButton(true);
} else {
setOverButton(false);
}
};
const limitedListener = useRef(throttle(mouseMoveListener, 1000));
const { updateState } = useContext(AppContext);
const showMenu = () => {
window.removeEventListener('mousemove', limitedListener.current);
setOverButton(false);
updateState('isMenuHidden', false);
}
return (
<div overButton className="floating-wrapper"><a className="floating-btn"
onMouseUp={e => handleMouseUp(e)}
onMouseDown={e => handleMouseDown(e)}
onTouchStart={e => handleTouchDown(e)}
onTouchEnd={e => handleTouchUp(e)}
style={{ top: position.y, left: position.x }}>
<img src="/imgs/action_add.svg" alt="Menu" className="menu-icon" /></a></div>
);
}

How to set a video current time on page load in HTML5 then add a seek listener synchronously

I have the following React component that grabs a video frame and loads the frame image into another image component upon specific events(pause, seek).
I wish to be able to set the current video frame position on rendering of the video element then add a seek event afterwards .
The problem I am experiencing is setting the frame position in componentDidMount with: video.currentTime = this.props.currentVideoFramePosition actually calls the seek event even though I added the event afterwards.
How can I modify the below code to set the current time of the video without triggering the seek event.
I am trying to avoid this on page load because the draw function is returning a value to another component which I do not want…….
import React from 'react';
export default class MyComponent extends React.Component {
constructor() {
super();
const self = this;
this.frameCaptureListener = function(e){
self.draw();
};
}
componentDidMount() {
var video = document.getElementById('video');
document.getElementById('video-canvas').style.display='none';
video.currentTime = this.props.currentVideoFramePosition;
video.addEventListener('pause', this.frameCaptureListener, false);
video.addEventListener('seeked', this.frameCaptureListener, false);
}
componentWillUnmount(){
var video = document.getElementById('video');
video.removeEventListener('pause', this.frameCaptureListener, false );
video.removeEventListener('seeked', this.frameCaptureListener, false);
}
draw(){
var video = document.getElementById('video');
var canvas = document.getElementById('video-canvas');
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = 1280;
canvas.height = 720;
context.drawImage( video, 0, 0, canvas.width, canvas.height);
var dataURL = canvas.toDataURL();
video.setAttribute('crossOrigin', 'anonymous');
var frameData = {imgSrc:dataURL, lastFramePosition:video.currentTime};
return this.props.onPause(frameData);
}
onArrowNavClick(navNum){
const self = this;
var video = document.getElementById('video');
video.currentTime = video.currentTime + navNum;
video.pause();
}
render(){
const videoUrl = this.props.videoUrl;
return(
<div>
<div className="framegrab-videocontainer">
<img src="/images/arrow_left_grab.svg" className="arrow-left" onClick={() => this.onArrowNavClick(-1)}></img>
<video id="video" src={videoUrl} className="framegrab-video" controls crossOrigin="anonymous" />
<img src="/images/arrow_right_grab.svg" className="arrow-right" onClick={() => this.onArrowNavClick(1)}></img>
</div>
<canvas id="video-canvas" ></canvas>
</div>
);
}
}
Here is how I am doing it with audio. Your problem with triggering seeking might be fixed by using a if (video.captureTime == props.captureTime) return; in frameCaptureListener.
import React from 'react';
import kshuffle, {knuthShuffle} from 'knuth-shuffle';
import JProgressBar from '../common/jProgressBar';
import PlayerActions from './player.actions';
let PlayerCtrlSty = {
backgroundColor: '#072202',
color: '#eaab5f',
margin: 'auto',
padding: '3px',
width: '320px'
}
let timerLeftSty = {minWidth: '40px'}
let timerRightSty = {textAlign: 'right', minWidth: '40px'}
let getTime = (time) => {
var minutes = time < 60 ? 0 : Math.floor(time / 60);
var seconds = Math.floor(time - (minutes * 60)) * .01;
return (minutes + seconds).toFixed(2);
}
class PlayerCtrlRender extends React.Component {
render() {
let index = this.state.playIndex;
let currentTrack = this.state.randomOn ? this.state.shuffledQueue[index] : this.props.queue[index];
let source = 'http://192.168.0.101:3950/play/' + currentTrack.location;
let title = currentTrack.title;
let progressData = {count: this.state.progressCount * 100, index: this.state.progressIndex * 100};
return (
<div id='PlayerCtrlSty' style={PlayerCtrlSty}>
<div className='FlexBox'>
<div style={timerLeftSty}>{this.state.progressIndex}</div>
<PlayerActions playing={this.state.isPlaying} clickHandler={this.clickHandler}/>
<div style={timerRightSty}>{this.state.progressCount}</div>
</div>
<JProgressBar data={progressData} position='none' />
<div id="title" style={{textAlign: 'center'}}>{title}</div>
<audio
ref="audioDiv"
src={source}
onDurationChange={this.onDurationChange}
onTimeUpdate={this.onTimeUpdate}
onEnded={this.nextSong}
/>
</div>
);
}
}
export default class PlayerCtrl extends PlayerCtrlRender {
constructor() {
super();
this.state = {
playIndex: 0,
queueLength: 1,
isPlaying: false,
progressCount: 0,
progressIndex: 0,
shuffledQueue: [{title: '', location: ''}],
randomOn: false
};
}
componentDidMount = () => {
let queue = knuthShuffle(this.props.queue.slice(0));
this.setState({queueLength: queue.length, shuffledQueue: queue});
this.refs.audioDiv.volume = .1;
};
clickHandler = (buttonid) => {
this.refs.audioDiv.autoplay = false;
switch (buttonid) {
case 'play': this.refs.audioDiv.play(); this.setState({isPlaying: true}); break;
case 'pause': this.refs.audioDiv.pause(); this.setState({isPlaying: false}); break;
case 'back': this.refs.audioDiv.currentTime = 0; break;
case 'next': this.nextSong(); break;
case 'random': this.refs.audioDiv.autoplay = this.state.isPlaying;
this.setState({randomOn: !this.state.randomOn}); break;
}
};
nextSong = () => {
this.refs.audioDiv.autoplay = this.state.isPlaying;
this.refs.audioDiv.currentTime = 0;
let newIndex = this.state.playIndex + 1;
if (newIndex == this.state.queueLength) newIndex = 0;
this.setState({playIndex: newIndex});
};
onDurationChange = () => {
let duration = this.refs.audioDiv.duration;
duration = getTime(Math.floor(duration));
this.setState({progressCount: duration})
this.setState({progressIndex: 0})
};
onTimeUpdate = () => {
let currentTime = this.refs.audioDiv.currentTime;
currentTime = getTime(Math.floor(currentTime));
this.setState({progressIndex: currentTime})
};
}