In JavaScript I re-order an element of a list if the mouseLeave `s it:
This works fine for the first leave-event. However the next time(s) the mouse leaves the element the leave-event is not triggered reliable.
My code is basically this (ES6 with reactJS):
onMouseDownOnListItem (index, event) {
this.draggedElementIndex = index
this.initialDragPos_Y = event.pageY
const listItem = this.refs[('ListItem_' + index)].getDOMNode()
listItem.addEventListener('mouseleave', this.onMouseLeaveOnListItem)
},
onMouseLeaveOnListItem (event) {
const delta = event.pageY - this.initialDragPos_Y
if (delta !== 0) {
let {elements} = this.state
let element = elements[this.draggedElementIndex]
let oldPos = this.draggedElementIndex
let newPos = -1
if (delta < 0) { // move element up
newPos = this.draggedElementIndex - 1
} else if (delta > 0) { // move element down
newPos = this.draggedElementIndex + 1
}
if (newPos >= 0 && newPos < elements.length) {
// move element in elements-array
elements.splice(oldPos, 1) // remove 1 element at oldPos
elements.splice(newPos, 0, element) // at newPos, delete 0, add element
this.draggedElementIndex = newPos
this.initialDragPos_Y = event.pageY
this.setState({
elements: elements
})
}
}
},
render () {
const {elements} = this.state
let content = []
elements.map((element, index) => {
let boundMouseDown = this.onMouseDownOnListItem.bind(this, index)
content.push(
<div ref={'ListItem_' + index} onMouseDown={boundMouseDown}>{element} </div>
)
})
return (
<div ref={'myList'}>
{content}
</div>
)
}
After the first leave-event you should call again onMouseDownOnListItem event in order to wait for another addEventListener.
Related
I'm trying to learn react and i creating a chess game.
I've to simulate a drag and click system to make the pieces move.
The function setActivePiece is called on mousedown, and it set the variable activePiece to the piece being clicked (only if it's a piece and not if it's the board being clicked), movePiece is only called on mouseup ( end of a click or end of a drag).
The drag system works, but i want to be able to click a piece and then click the position where i want the piece to move.
After a click, the activePiece is set to the piece clicked (using e.target) and the function movePiece is called (cause a click call both setActivePiece and movePiece), but during the second click the value of the Variable activePiece is null, in fact the piece wont move.
Any suggestions?
Here's the code.
function Board(){
const [pieces, setPieces] = useState([]);
const boardRef = useRef();
let activePiece = null;
function movePiece(e) {
if (activePiece === null)
return;
//determinate the position in the board (from cordinate to index of the board)
setPieces(prevState => {
const updatePieces = prevState.map(p => {
//finding the right piece
if (p.id === activePiece.id){
let lenghtmovingPiece = boardRef.current.offsetWidth / 8;
//aggiorno la posizione (int(posmovingPiece - posBoard / lenghtmovingPiece))
p.x = parseInt((e.clientX - boardRef.current.getBoundingClientRect().x) / lenghtmovingPiece);
p.y = parseInt((e.clientY - boardRef.current.getBoundingClientRect().y) / lenghtmovingPiece);
}
return p;
})
//reset hte active piece
activePiece = null
return updatePieces;
})
}
function setActivePiece(e) {
//no preview image
e.preventDefault();
//click of the board
if (e.target.id === "boardImg")
return;
activePiece = e.target
}
function dragPiece(e){
//nothing selected
if (activePiece === null)
return;
//set the element new position when dragged
//newPos = mousePos - spazio tra la finestra e la scacchiera (perche' i piece sono absolute alla scacchiera) - meta' della grandezza del piece (= larghezzaBoard / 4)
activePiece.style.left = e.clientX - boardRef.current.getBoundingClientRect().x - boardRef.current.getBoundingClientRect().x/4 + "px";
activePiece.style.top = e.clientY - boardRef.current.getBoundingClientRect().y - boardRef.current.getBoundingClientRect().x/4 + "px";
}
useEffect(() => {
// createMatch("matches", "match0/logs/log1", {})
// createMatch("matches", "match0", objBoard)
setPieces(generateBoard())
}, [activePiece]);
return(
<div id="board" onMouseDown={setActivePiece} onMouseUp={movePiece} onMouseMove={dragPiece} >
<img ref={boardRef} id="boardImg" src={boardImg} alt="board" draggable="false"/>
{
//pieces of the board
pieces.map(piece => return <Piece key={uuidv4()} piece={piece}/>)
}
</div>
)
}
Also tried to use an useState but i dont know if it's the right path.
Yes, useState is the right path. Consider this slight revision:
export default function Board() {
const [activePiece, setActivePiece] = useState(null);
const [pieces, setPieces] = useState([]);
const boardRef = useRef();
function movePiece(e) {
if (activePiece === null) return;
//determinate the position in the board (from cordinate to index of the board)
setPieces((prevState) => {
const updatePieces = prevState.map((p) => {
//finding the right piece
if (p.id === activePiece.id) {
let lenghtmovingPiece = boardRef.current.offsetWidth / 8;
//aggiorno la posizione (int(posmovingPiece - posBoard / lenghtmovingPiece))
p.x = parseInt(
(e.clientX - boardRef.current.getBoundingClientRect().x) /
lenghtmovingPiece
);
p.y = parseInt(
(e.clientY - boardRef.current.getBoundingClientRect().y) /
lenghtmovingPiece
);
}
return p;
});
//reset the active piece
setActivePiece(null);
return updatePieces;
});
}
function onMouseDown(e) {
//no preview image
e.preventDefault();
//click of the board
if (e.target.id === "boardImg") return;
setActivePiece(e.target);
}
function dragPiece(e) {
//nothing selected
if (activePiece === null) return;
const updatedActivePiece = { ...activePiece };
//set the element new position when dragged
//newPos = mousePos - spazio tra la finestra e la scacchiera (perche' i piece sono absolute alla scacchiera) - meta' della grandezza del piece (= larghezzaBoard / 4)
updatedActivePiece.style.left =
e.clientX -
boardRef.current.getBoundingClientRect().x -
boardRef.current.getBoundingClientRect().x / 4 +
"px";
updatedActivePiece.style.top =
e.clientY -
boardRef.current.getBoundingClientRect().y -
boardRef.current.getBoundingClientRect().x / 4 +
"px";
setActivePiece(updatedActivePiece);
}
useEffect(() => {
// createMatch("matches", "match0/logs/log1", {})
// createMatch("matches", "match0", objBoard)
setPieces(generateBoard());
}, [activePiece]);
return (
<div
id="board"
onMouseDown={onMouseDown}
onMouseUp={movePiece}
onMouseMove={dragPiece}
>
<img
ref={boardRef}
id="boardImg"
src={boardImg}
alt="board"
draggable="false"
/>
{
//pieces of the board
pieces.map((piece) => (
<Piece key={uuidv4()} piece={piece} />
))
}
</div>
);
}
This function detects whether the content enters the screen or not. I'm trying to return true or false depending on these conditions. but it returns undefined value. Since I will use this function in high order later, I need to output true or false.
function poser(x) {
$(document).scroll(function(){
let wHeight = $(window).height();
let cHeight = $(x).outerHeight();
let scroll = $(window).scrollTop();
let contentPos = $(x).position();
let contentTop = contentPos.top;
let inScreen = (parseInt(scroll) + parseInt(wHeight)) /* - wHeight / 2 */ >= parseInt(contentTop);
let outScreen = parseInt(contentTop) + parseInt(cHeight) <= scroll;
if (inScreen && !outScreen) {
return true
}
if (inScreen && outScreen) {
return false
}
})
}
console.log(poser(".sectionOne"))
body {
height:3000px}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="sectionOne">
test
</div>
You should be able to get the element and then from there the code is pretty simple
var myElement = document.getElementById('my-element');
function elementInViewport() {
var bounding = myElement.getBoundingClientRect();
if (bounding.top >= 0
&& bounding.left >= 0
&& bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
&& bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight)) {
alert('Element is in the viewport!');
} else {
alert('Element is NOT in the viewport!');
}
}
Then use a button and call the function elementInViewport()
<button onclick='elementInViewPort()'>Check If Element In View</button>
https://codepen.io/andreaswik/pen/dxVMvj
I hope this will help you it doesn't return true or false but you can add a callback function that runs your code if the content is intersecting or not.
// This is your observer that checks if your element is in or out of your viewport
function poser(elem, myCallback) {
let observer = new IntersectionObserver(
entries => {
entries.forEach(entry => {
// this is where your callback function runs and receives the true or false
entry.isIntersecting ? myCallback(entry.isIntersecting): myCallback(entry.isIntersecting);
});
},
{
root: null,
rootMargin: "0% 0% 0% 0%",
threshold: 1,
}
);
observer.observe(elem);
}
// In this callback function you right the code you want to execute if the element is intersection or not
function myCallback(state) {
state ? console.log("i intersect") : console.log("now i don't");
}
poser(document.querySelector("Your selector"), myCallback);
So I am creating a game where a spaceship moves and it must avoid a fireball image in order to win. My issue is that I have only one fireball looping over and over. Instead, I would like to have many fireballs, which are multiplied as time passes. I think I should need to incorporate an array and use push() method but I tried and it didn't worked. If anyone could help me, it would be very appreciated. Thanks
//Fireball script
function fireballScript(offset) {
return Math.floor(Math.random() * (window.innerWidth - offset))
}
let fireballMovement = {
x: fireballScript(fireball.offsetWidth),
y: 0
}
const fireLoop = function () {
fireballMovement.y += 1
fireball.style.top = fireballMovement.y + 'px'
if (fireballMovement.y > window.innerHeight) {
fireballMovement.x = fireballScript(fireball.offsetWidth)
fireball.style.left = fireballMovement.x + 'px'
fireballMovement.y = 0
fireball.setAttribute('hit', false)
}
}
fireball.style.left = fireballMovement.x + 'px'
let fireballSpeed = setInterval(fireLoop, 1000 / 250)
let fireball = document.querySelector("#fireball")
<img src = "Photo/fireball.png" id = "fireball" >
//Stop game on collision
function checkCollision() {
if (detectOverlap(spaceship, fireball) && fireball.getAttribute('hit') == 'false') {
hits++
fireball.setAttribute('hit', true)
alert("lost")
}
setTimeout(checkCollision, 1)
}
var detectOverlap = (function () {
function getPositions(spaceship) {
var pos = spaceship.getBoundingClientRect()
return [[pos.left, pos.right], [pos.top, pos.bottom]]
}
function comparePositions(p1, p2) {
let r1 = p1[0] < p2[0] ? p1 : p2
let r2 = p1[0] < p2[0] ? p2 : p1
return r1[1] > r2[0] || r1[0] === r2[0]
}
return function (a, b) {
var pos1 = getPositions(a),
pos2 = getPositions(b)
return comparePositions(pos1[0], pos2[0]) && comparePositions(pos1[1], pos2[1])
}
})()
let spaceship = document.querySelector("#icon")
<img src = "Photo/Spaceship1.png" id="icon">
Ps: I asked this question a dozen of times but I could never get an answer which is incorporate into my code. It would be very helpful if someone could fix this... thanks again
You need to have an array of fireballs
var fireballs = [];
Make a constructor for fireballs instead of putting them directly in the html
function fireball(x, y) {
movementX = x;
movementY = y;
}
Then push new ones into the array with dynamic position values. To add them to the document, you need to append them to a parent element. If the <body> is that parent, you'd do this:
let f = new fireball(10, 20)
fireballs.push(f)
document.body.appendChild(f)
In your update, iterate through the fireballs and update their movement.
fireballs.forEach((fireball) => {
// update position for each fireball
});
I'm trying to implement a virtual list in the same vein as React-Virtualized.
I've got it working for the most part however when you scroll past a certain threshold, the list continues to scroll by itself without user input, until it reaches the end of the list. What am I doing that causes this?
Here's a Codepen.
The Component in question is the VirtualList component.
const todoHeight = 40;
const viewport = { height: 800, width: 750 };
const itemsAllowedInView = viewport.height / todoHeight;
const numItems = 10000;
const maxScrollItems = numItems - itemsAllowedInView;
const maxScrollTop = todoHeight * maxScrollItems;
class VirtualList extends React.Component {
constructor(props) {
super(props);
this.state = {
currentIndex: 0,
amountRowsToRender: itemsAllowedInView * 2,
topPadding: 0,
bottomPadding: maxScrollTop - viewport.height
}
this.lastTop = 0;
}
scrollFunction(currentTop) {
if (this.lastTop === currentTop || currentTop >= maxScrollTop || currentTop < 0) { return; }
let currentIndex = Math.floor(currentTop / todoHeight);
let endIndex = Math.min(currentIndex + itemsAllowedInView, numItems);
// We want to render some extra for padding.
let bufferItems = itemsAllowedInView / 2;
let bufferSize = bufferItems * todoHeight;
// Create top Padding
let topPaddingHeight = 0;
let bottomPaddingHeight = 0;
topPaddingHeight = Math.max(0, currentTop - (bufferItems * todoHeight));
// bottomPaddingHeight = Math.min((currentTop - (bufferItems * todoHeight)) + itemsAllowedInView, maxScrollTop);
bottomPaddingHeight = Math.min(maxScrollTop - currentTop - viewport.height - (bufferItems * todoHeight), maxScrollTop);
this.lastTop = currentTop;
this.setState({
currentIndex: currentIndex,
topPadding: topPaddingHeight >= 0 ? topPaddingHeight : 0,
bottomPadding: bottomPaddingHeight >= 0 ? bottomPaddingHeight : 0,
})
}
catchScroll(e) {
this.scrollFunction.call(this, e.target.scrollTop);
}
paddingDiv(height) {
return (<div className='todo' style={{ height: height + 'px' }} />);
}
paddingTop() {
// if(this.state.topPadding > 0){
return (this.paddingDiv(this.state.topPadding));
// }
}
paddingBottom() {
// if(this.state.bottomPadding > 0){
return (this.paddingDiv(this.state.bottomPadding));
// }
}
render() {
return (
<div
className={'list-container'}
onScroll={this.catchScroll.bind(this)}
>
<div className={'inner-list'}>
{this.paddingTop.call(this)}
{!!this.props.todos && this.props.todos.slice(this.state.currentIndex, this.state.currentIndex + this.state.amountRowsToRender).map((todo, i) =>
<Todo content={todo.content}
height={todo.height ? todo.height : null}
id={i}
key={i}
completed={todo.completed}
/>
)}
{this.paddingBottom.call(this)}
</div>
</div>
)
}
}
I have more than 10 images in list and 2 arrows icon to scroll up and down to show image (just like a carousel). Now I want to disable arrow up/ down when no more image present in list it's respective direction. Also enable these arrows when next element present.
here is my code.
function favscroll(direction){
var scrolltargetid= 'favorite-link-wrapper';
var scrolltarget = document.getElementById(scrolltargetid);
var scrolltargetheight= scrolltarget.scrollHeight;
if(direction=='down'){
$("#arrow-navup > a").removeClass('up');
var targetposition = scrolltarget.scrollTop + scrolltarget.clientHeight;
var distance=0;
scrollintervall = setInterval (function () {
$("#arrow-navdown").unbind("click");
distance += 3;
scrolltarget.scrollTop= scrolltarget.scrollTop + distance;
if ( (scrolltarget.scrollTop >= (targetposition)) || ( scrolltarget.scrollTop >= scrolltarget.scrollHeight-scrolltarget.clientHeight) ){
$("#arrow-navdown").bind("click", function(event) {
event.stopPropagation();
favscroll("down");
$("#arrow-navdown > a").addClass('down');
});
clearInterval (scrollintervall);
}
}, 20);
}
if(direction=='up'){
$("#arrow-navdown > a").removeClass('down');
var targetposition = scrolltarget.scrollTop - scrolltarget.clientHeight;
var distance=0;
scrollintervall = setInterval (function () {
$("#arrow-navup").unbind("click");
distance += 3;
scrolltarget.scrollTop= scrolltarget.scrollTop - distance;
if ( (scrolltarget.scrollTop <= (targetposition)) || ( scrolltarget.scrollTop <= 0) ){
$("#arrow-navup").bind("click", function(event) {
event.stopPropagation();
favscroll("up");
$("#arrow-navup > a").addClass('up');
});
clearInterval (scrollintervall);
}
}, 20);
}
}