I'm aiming to retrieve the full scrollable height of my page using:
document.body.scrollHeight
but all I get back is the height of the viewport and not the full height of the scrollable page.
I'm running this in a method that is called after mounted. It's even in a nexttick function. I need this to randomly place images over the full height of the page. It could be something to do with Vue's reactivity(?) and the DOM not updating quick enough for my method to get the full document height?
What is a different way I can get the scroll height of a document in Vue that will actually work?
<script>
export default {
data() {
return {
images: [
require("#/assets/img/icons/1.png"),
require("#/assets/img/icons/2.png"),
],
addedImage: [],
imgTop: 150,
imgLeft: 900,
imgHeight: 64,
imgWidth: 64,
selectedImage: "",
nextId: 100,
};
},
mounted() {
this.randomImage();
this.$nextTick(() => {
for (let i = 0; i < 40; i++) {
this.randomImage();
this.addImage();
this.randomPosition();
}
});
},
methods: {
randomImage() {
const idx = Math.floor(Math.random() * this.images.length);
this.selectedImage = this.images[idx];
},
randomPosition() {
const randomPos = (twoSizes) => Math.round(Math.random() * twoSizes);
this.imgTop = randomPos(document.body.scrollHeight - this.imgHeight);
this.imgLeft = randomPos(window.innerWidth - this.imgWidth);
},
addImage() {
this.addedImage.push({
style: {
top: `${this.imgTop}px`,
left: `${this.imgLeft}px`,
height: `${this.imgHeight}px`,
width: `${this.imgWidth}px`,
},
src: this.selectedImage,
id: this.nextId++,
});
},
},
};
</script>
Related
I wanted to make a div of random positioned icons and this is what i made so far.
Can we make this to generate only limited number of images like 20 images and not unlimited.
If you have better way to make something like this, then i would be really thankful.
Thanks
let nextId = 20
new Vue({
el: '#app',
data() {
return {
images: [
'//placekitten.com/200/200',
'//placekitten.com/200/201',
'//placekitten.com/200/202',
'//placekitten.com/200/203',
'//placekitten.com/200/204',
],
addedImage: [],
imgTop: -100,
imgLeft: -100,
imgHeight: 64,
imgWidth: 64,
changeInterval: 10,
selectedImage: ''
}
},
created() {
this.randomImage();
const randomImg = func => setInterval(func, this.changeInterval);
randomImg(this.randomImage);
randomImg(this.addImage);
randomImg(this.randomPosition);
},
methods: {
randomImage() {
const idx = Math.floor(Math.random() * this.images.length);
this.selectedImage = this.images[idx];
},
randomPosition() {
const randomPos = twoSizes => Math.round(Math.random() * twoSizes);
this.imgTop = randomPos(window.innerHeight - this.imgHeight);
this.imgLeft = randomPos(window.innerWidth - this.imgWidth);
},
addImage(){
this.addedImage.push({
style: {
top: `${this.imgTop}px`,
left: `${this.imgLeft}px`,
height: `${this.imgHeight}px`,
width: `${this.imgWidth}px`
},
src: this.selectedImage,
id: nextId++
});
},
}
})
.image {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<img :style="image.style" class="image" :key="image.id" :src="image.src" v-for="image in addedImage">
</div>
Introduce variables for the total, the current count, and the interval reference:
limit: 20,
counter: 0,
interval: null
Combine the three calls to setInterval into one and store the interval.
created() {
this.interval = setInterval(() => {
this.randomImage();
this.randomPosition();
this.addImage();
this.counter++;
if (this.counter === this.limit) {
clearInterval(this.interval);
}
}, this.changeInterval);
},
Each call increases the counter and when the limit is reached, the interval is cleared. Here's a demo:
let nextId = 20
new Vue({
el: '#app',
data() {
return {
images: [
'//placekitten.com/200/200',
'//placekitten.com/200/201',
'//placekitten.com/200/202',
'//placekitten.com/200/203',
'//placekitten.com/200/204',
],
addedImage: [],
imgTop: -100,
imgLeft: -100,
imgHeight: 64,
imgWidth: 64,
changeInterval: 10,
selectedImage: '',
limit: 20,
counter: 0,
interval: null
}
},
created() {
this.interval = setInterval(() => {
this.randomImage();
this.randomPosition();
this.addImage();
this.counter++;
if (this.counter === this.limit) {
clearInterval(this.interval);
}
}, this.changeInterval);
},
methods: {
randomImage() {
const idx = Math.floor(Math.random() * this.images.length);
this.selectedImage = this.images[idx];
},
randomPosition() {
const randomPos = twoSizes => Math.round(Math.random() * twoSizes);
this.imgTop = randomPos(window.innerHeight - this.imgHeight);
this.imgLeft = randomPos(window.innerWidth - this.imgWidth);
},
addImage(){
this.addedImage.push({
style: {
top: `${this.imgTop}px`,
left: `${this.imgLeft}px`,
height: `${this.imgHeight}px`,
width: `${this.imgWidth}px`
},
src: this.selectedImage,
id: nextId++
});
},
}
})
.image {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<img :style="image.style" class="image" :key="image.id" :src="image.src" v-for="image in addedImage">
</div>
I solved this by adding a condition with a array length.
Length can be defined with any number of images we want. For example if we want 20 images then add this:
addImage(){
if (this.addedImage.length < 20) {
this.addedImage.push({
style: {
top: `${this.imgTop}vh`,
left: `${this.imgLeft}vw`,
height: `${this.imgHeight}px`,
width: `${this.imgWidth}px`
},
src: this.selectedImage,
id: nextId++
});
}
}
I'm trying to create a snake game in Phaser 3. The arrow.(key).isDown is acting strangely and I can't figure out why. If you press a key weather it is up, down, left or right it will not change directions if you press the opposite, it just keeps moving. like if I press down it keeps going down even after I press up. I have been trying to debug it for hours. All the examples pretty much have it set up the same way.
This is my code so far
class mainScene {
preload() {
this.load.image("fruit", "images/fruit.png");
this.load.image("snake", "images/snake.png");
}
create() {
this.score = 0;
this.snake = this.physics.add.sprite(20, 20, "snake");
this.fruit = this.physics.add.sprite(
Phaser.Math.Between(10, 590),
Phaser.Math.Between(10, 590),
"fruit"
);
this.scoreText = this.add.text(500, 5, `Score: ${this.score}`);
this.arrow = this.input.keyboard.createCursorKeys();
//debug
}
update() {
if (this.physics.overlap(this.snake, this.fruit)) {
this.hit();
}
this.snake.setVelocity(0);
if (this.arrow.right.isDown) {
this.snake.setVelocityX(50);
if (this.snake.x > 600) {
this.outOfBounds();
}
} else if (this.arrow.left.isDown) {
this.snake.setVelocityX(-50);
if (this.snake.x < 0) {
this.outOfBounds();
}
}
if (this.arrow.down.isDown) {
this.snake.setVelocityY(50);
if (this.snake.y > 600) {
this.outOfBounds();
}
} else if (this.arrow.up.isDown) {
this.snake.setVelocityY(-50);
if (this.snake.y < 0) {
this.outOfBounds();
}
}
}
hit() {
this.fruit.x = Phaser.Math.Between(10, 590);
this.fruit.y = Phaser.Math.Between(10, 590);
this.score += 1;
this.scoreText.setText(`Score: ${this.score}`);
this.tweens.add({
targets: this.snake,
duration: 200,
scaleX: 1.2,
scaleY: 1.2,
yoyo: true,
});
}
outOfBounds() {
this.score = 0;
this.scoreText.setText(`Score: ${this.score}`);
this.gameOverText = this.add.text(
100,
250,
"You went out of bounds.\nGame Over\nPress Space to continue.",
{ fontSize: "24px" }
);
if (this.arrow.space.isDown) {
this.gameOverText.destroy();
this.scene.restart();
}
}
}
const config = {
type: Phaser.CANVAS,
width: 600,
height: 600,
backgroundColor: 0x000040,
scene: mainScene,
physics: {
default: "arcade",
debug: true,
setBounds: { x: 0, y: 0, width: 600, height: 600 },
},
parent: "game",
};
new Phaser.Game(config);
When creating a path using dagre, the whole nodes accumulate in one position. How can we set default positions for nodes ( Cytoscape js without react works fine) instead of setting position separately using position attribute for nodes.
const layout = {
name: "dagre",
rankDir: "LR"
}
pageData = < CytoscapeComponent
elements = {
CytoscapeComponent.normalizeElements({
nodes: nodess,
edges: edgess,
layout: layout,
})
}
pan = {
{
x: 200,
y: 200
}
}
autounselectify = {
true
}
userZoomingEnabled = {
false
}
boxSelectionEnabled = {
false
}
style = {
{
width: "1200px",
height: "1000px"
}
}
/>
return (
< div
{
pageData
}
< /div>
);
Expected result:
Current result:
There is a way to force the calculation of node positions as they are added. This is also useful when the elements of the graph change dynamically with the state of the component and the graph has to be rendered again with updated node positions.
<CytoscapeComponent
cy={(cy) => {
this.cy = cy
cy.on('add', 'node', _evt => {
cy.layout(this.state.layout).run()
cy.fit()
})
}}
/>
Here is what worked for me:
cytoscape.use(fcose)
//..then in my render...
<CytoscapeComponent
elements={elements1}
layout={{
animate: true,
animationDuration: undefined,
animationEasing: undefined,
boundingBox: undefined,
componentSpacing: 40,
coolingFactor: 0.99,
fit: true,
gravity: 1,
initialTemp: 1000,
minTemp: 1.0,
name: 'fcose',
nestingFactor: 1.2,
nodeDimensionsIncludeLabels: false,
nodeOverlap: 4,
numIter: 1000,
padding: 30,
position(node) {
return { row: node.data('row'), col: node.data('col') }
},
randomize: true,
refresh: 20,
}}
style={{ width: '600px', height: '300px' }}
/>
You may try "Euler" layout instead of "Dagre" layout
I'm having trouble when initializing the position for my custom draggable directive. I was supposed to make its position on the center of the screen. However, after I calculating, it only binds on what is on the data() which is (0,0). Here is my code below:
export default {
directives: {
Drag,
},
data: function() {
return {
position: {
x: 0,
y: 0,
},
window: {
width: 0,
height: 0,
},
}
},
created() {
window.addEventListener('resize', this.handleResize)
this.handleResize()
},
destroyed() {
window.removeEventListener('resize', this.handleResize)
},
methods: {
handleResize() {
this.window.width = window.innerWidth
this.window.height = window.innerHeight
},
},
mounted: function() {
this.position.x = this.window.width / 2
this.position.y = this.window.height / 2
},
}
On HTML:
<div class="modal-wrapper" v-drag="position">
I'm building a web app and there's a part where if a button is clicked, this animation chain will execute:
var textReference = Ext.getCmp('splashText');
var checker = Ext.getCmp('arrow');
var img;
//for some reason, the logic is flipped
if(checker){
img = Ext.getCmp('arrow');
}
else{
console.log('checker is defined');
img = Ext.create('Ext.window.Window', {
header: false,
style: 'background-color: transparent; border-width: 0; padding: 0',
bodyStyle: 'background-color: transparent; background-image: url(graphics/arrow_1.png); background-size: 100% 100%;',
width: 70,
id: 'arrow',
height: 70,
border: false,
bodyBorder: false,
frame: false,
cls: 'noPanelBorder',
x: textReference.getBox().x - 90,
y: textReference.getBox().y - 10,
shadow: false,
});
}
img.show();
var origW = img.getBox().width,
origH = img.getBox().height,
origX = img.getBox().x,
origY = img.getBox().y;
//go down
Ext.create('Ext.fx.Anim', {
target: img,
duration: 500,
delay: 0,
from: {
x: textReference.getBox().x - 90,
y: textReference.getBox().y - 10,
},
to: {
y: origY + 180,
opacity: 1,
}
});
//bounce up 1st
Ext.create('Ext.fx.Anim', {
target: img,
duration: 500,
delay: 500,
from: {
},
to: {
y: origY + 50,
}
});
//fall down 1st
Ext.create('Ext.fx.Anim', {
target: img,
duration: 500,
delay: 1000,
from: {
},
to: {
y: origY + 180,
}
});
//bounce up 2nd
Ext.create('Ext.fx.Anim', {
target: img,
duration: 500,
delay: 1500,
from: {
},
to: {
y: origY + 100,
}
});
//fall down 2nd
Ext.create('Ext.fx.Anim', {
target: img,
duration: 500,
delay: 2000,
from: {
},
to: {
y: origY + 180,
}
});
//fade out
Ext.create('Ext.fx.Anim', {
target: img,
duration: 1000,
delay: 3500,
from: {
},
to: {
x: textReference.getBox().x - 90,
y: textReference.getBox().y - 10,
opacity: 0
}
});
It's just a basic animation where a "ball" will drop, then bounce up and down that makes it look like its bouncing up then down twice before staying at the bottom, then fades out.
It's working well, however, if the user clicks on the button over and over again while the previous animation chain is still animating, the animations will compound and the calculations for the positions will get compounded, making the ball appear much lower than it should.
To prevent this, what I want to happen is once the button gets clicked, the whole animation chain gets cancelled first, then the animation starts from the top. This is to ensure that any existing animation will get halted and then a fresh sequence will get started.
Does anyone have any idea on how to execute this? I've tried stopAnimation() but nothing happens.
The problem with ball dropping lower and lower is that you define original Y as textReference.getBox().y - 10 and then you try to move it to a different original Y + 180. Set your original Y first as textReference.getBox().y - 10 and then just use it for window placement, start of the aniamtion and other animation parts.
Secondly, you should use img.animate() instead of
Ext.create('Ext.fx.Anim', {
target: img,
If you do that, you can then use the stopAnimation() method you mentioned.
Example code:
var windowId = 'basketBallAnimation';
var img = Ext.getCmp( windowId );
// Set whatever you want here and use it
var originalX = 30;
var originalY = 30;
if( !img )
{
console.log('creating new image');
img = Ext.create('Ext.window.Window', {
header: false,
style: 'background-color: transparent; border-width: 0; padding: 0',
bodyStyle: 'background-color: transparent; background-image: url(//ph-live-02.slatic.net/p/6/molten-official-gr7-fiba-basketball-orange-4397-1336487-66ec7bf47676dee873cbb5e8131c4c1f-gallery.jpg); background-size: 100% 100%;',
width: 70,
height: 70,
id: windowId,
border: false,
bodyBorder: false,
frame: false,
cls: 'noPanelBorder',
x: originalX,
y: originalY,
shadow: false,
});
}
img.stopAnimation();
img.show();
// go down
img.animate({
duration: 500,
from: {
x: originalX,
y: originalY,
},
to: {
y: originalY + 180,
}
});
// ...
Working fiddle here.
Other off-topic comments on your coding style:
There's no need to save the image window to 'checker' and if the 'checker' exists to call Ext.getCmp() again, it saves time just to get the image and create it, if it is not saved.
Generally use variables more to store your data instead of calling the same method numerous times (textReference.getBox().x - 90 is called three times just in your small snippet. If you ever want to change it, you have to look really carefully where else it is applied).
If at all possible, avoid defining any style in ExtJS. Add cls configuration with CSS class and apply your styles in separate CSS file.