I would like to implement the following: The animation should only start when I hover the mouse over the div. After I hovered over the div, the end number should remain visible and not change to the start value.
This is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Animation</title>
<style>
.counter{
color: white;
font-size: 100px;
height: 300px;
width: 400px;
background-color: black;
display: flex;
align-items:center;
justify-content: center;
}
.animate{
position:absolute;
opacity:0;
transition:0s 180s;
}
.animate:hover {
opacity:1;
transition:0s;
}
</style>
</head>
<body>
<div id="animate" style="background-color: orange; width: 300px; height: 200px;" class="counter" data-target="500">0</div>
<script>
const counters = document.querySelectorAll('.counter');
for(let n of counters) {
const updateCount = () => {
const target = + n.getAttribute('data-target');
const count = + n.innerText;
const speed = 5000; // change animation speed here
const inc = target / speed;
if(count < target) {
n.innerText = Math.ceil(count + inc);
setTimeout(updateCount, 1);
} else {
n.innerText = target;
}
}
updateCount();
}
</script>
</body>
</html>
Add onmousover to id="animate"
<div id="animate" style="background-color: orange; width: 300px; height: 200px;" class="counter" data-target="500" onmouseover="animationEffect();">0</div>
Wrap the whole script in a method:
function animationEffect(){
const counters = document.querySelectorAll('.counter');
for(let n of counters) {
const updateCount = () => {
const target = + n.getAttribute('data-target');
const count = + n.innerText;
const speed = 5000; // change animation speed here
const inc = target / speed;
if(count < target) {
n.innerText = Math.ceil(count + inc);
setTimeout(updateCount, 1);
} else {
n.innerText = target;
}
}
updateCount();
}
}
Should solve the problem
EDIT:
The old answer was refering to the question before being edited. For the current case the following could be done:
const updateCount = n => {
const target = +n.getAttribute('data-target')
const count = +n.innerText
const speed = 5000 // change animation speed here
const inc = target / speed
if (count < target) {
n.innerText = Math.ceil(count + inc)
requestAnimationFrame(() => updateCount(n))
} else {
n.innerText = target
}
}
const counters = document.querySelectorAll('.counter')
for (let n of counters) {
n.addEventListener('mouseenter', () => updateCount(n), {
once: true
})
}
.counter {
color: white;
font-size: 100px;
height: 300px;
width: 400px;
background-color: black;
display: flex;
align-items: center;
justify-content: center;
}
.animate {
position: absolute;
opacity: 0;
transition: 0s 180s;
}
.animate:hover {
opacity: 1;
transition: 0s;
}
<div id="animate" style="background-color: orange; width: 300px; height: 200px" class="counter" data-target="500">
0
</div>
Old answer:
You would need to add a mouseenter event to the parent element. Note that the {once: true} option will make the event only fire once.
const parent = document.getElementById('parent')
parent.addEventListener('mouseenter', mouseEnterHandler, {once: true})
Then define the mouseEnterHandler callback as follows:
function mouseEnterHandler() {
for (let n of counters) {
n.style.display = 'block'
updateCount(n)
}
/* If you only have one counter then just get it by its Id:
const div = document.getElementById('hover-content')
div.style.display = 'block'
updateCount(div)
*/
}
n.style.display = 'block' will make the counter visible so no need for the css rule #parent:hover #hover-content { display:block; }.
Here is a working example:
const updateCount = n => {
const target = +n.getAttribute('data-target')
const count = +n.innerText
const speed = 5000 // change animation speed here
const inc = target / speed
if (count < target) {
n.innerText = Math.ceil(count + inc)
requestAnimationFrame(() => updateCount(n))
} else {
n.innerText = target
}
}
const counters = document.querySelectorAll('.counter')
const parent = document.getElementById('parent')
parent.addEventListener('mouseenter', mouseEnterHandler, {
once: true
})
function mouseEnterHandler() {
for (let n of counters) {
n.style.display = 'block'
updateCount(n)
}
}
.counter {
color: white;
font-size: 100px;
height: 140px;
width: 400px;
background-color: black;
display: flex;
align-items: center;
justify-content: center;
}
#hover-content {
display: none;
}
<div id="parent">
Some content
<div hidden id="hover-content" class="counter" data-target="232">0</div>
</div>
Related
So what I am trying to do is, animate using javascript. It is working fine but I also want to show the value of pos(variable) in code, to show the value gradually.
problem:
the value of demo2 is not changing even the scope of the variable is same. why not #demo2 not changing.
Please let me know the reason and solution that value of the id #demo2 also change as the value of id #demo changing.
<!DOCTYPE html>
<html>
<style>
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#animate {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
</style>
<body>
function myMove() {
let id = null;
const elem = document.getElementById("animate");
let pos = 0;
clearInterval(id);
id = setInterval(frame, 5);
function frame() {
if (pos == 350) {
clearInterval(id);
} else {
pos++;
elem.style.top = pos + "px";
elem.style.left = pos + "px";
}
document.getElementById("demo").innerHTML = pos;
}
document.getElementById("demo2").innerHTML = pos;
}
</script>
<style>
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#animate {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
</style>
<p><button onclick="myMove()">Click Me</button></p>
<div id ="container">
<div id ="animate"></div>
</div>
<p id="demo"></p>
<p id="demo2"></p>
function myMove() {
let id = null;
const elem = document.getElementById("animate");
let pos = 0;
clearInterval(id);
id = setInterval(frame, 5);
function frame() {
if (pos == 350) {
clearInterval(id);
} else {
pos++;
elem.style.top = pos + "px";
elem.style.left = pos + "px";
}
document.getElementById("demo").innerHTML = pos;
}
document.getElementById("demo2").innerHTML = pos;
}
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#animate {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
<p><button onclick="myMove()">Click Me</button></p>
<div id ="container">
<div id ="animate"></div>
</div>
<p id="demo"></p>
<p id="demo2"></p>
<p><button onclick="myMove()">Click Me</button></p>
<div id="container">
<div id="animate"></div>
</div>
<p id="demo"></p>
<p id="demo2"></p>
<script>
function myMove() {
let id = null;
const elem = document.getElementById("animate");
let pos = 0;
clearInterval(id);
id = setInterval(frame, 5);
function frame() {
if (pos == 350) {
clearInterval(id);
} else {
pos++;
elem.style.top = pos + "px";
elem.style.left = pos + "px";
}
document.getElementById("demo").innerHTML = pos;
}
document.getElementById("demo2").innerHTML = pos;
}
</script>
</body>
</html>
The function frame() is executed after every other instructions in your function myMove().
If you want to change the value of #demo2 you have to put it in the same scope as the function frame() because that is where the value is updated. Otherwise you will get the value pos has when it is initialized.
function myMove() {
let id = null;
const elem = document.getElementById("animate");
let pos = 0;
clearInterval(id);
id = setInterval(frame, 5);
function frame() {
if (pos == 350) {
clearInterval(id);
} else {
pos++;
elem.style.top = pos + "px";
elem.style.left = pos + "px";
}
document.getElementById("demo").innerHTML = pos;
document.getElementById("demo2").innerHTML = pos;
}
}
I am trying to make an animation that scrolls from left to right the child elements of a div, I have looked around but none of the other questions with answers seem to work, so far this is what I have accomplished:
Home.css:
#keyframes scroll {
20% {
transform: translateX(-100vw);
}
40% {
transform: translateX(-200vw);
}
60% {
transform: translateX(-300vw);
}
80% {
transform: translateX(-400vw);
}
100% {
transform: translateX(0vw);
}
}
.section {
display: flex;
flex-direction: row;
font-family: Arial, sans-serif;
font-weight: bold;
transition: 1s;
width: 100vw;
height: 100vh;
animation-name: scroll;
animation-duration: 1s;
}
.child1 {
background-color: #c0392b;
flex: none;
width: 100vw;
height: 100vh;
}
.child2 {
background-color: #e67e22;
flex: none;
width: 100vw;
height: 100vh;
}
.child3 {
background-color: #27ae60;
flex: none;
width: 100vw;
height: 100vh;
}
.child4 {
background-color: #2980b9;
flex: none;
width: 100vw;
height: 100vh;
}
.child5 {
background-color: #8e44ad;
flex: none;
width: 100vw;
height: 100vh;
}
Home.jsx:
import { forwardRef, useEffect, useRef, useState } from "react";
import { Link } from "react-router-dom";
import './Home.css';
const Child1 = forwardRef((props, ref) => {
return <div className="child1">
<h1>Child1</h1>
</div>;
});
const Child2 = forwardRef((props, ref) => {
return <div className="child2">
<h1>Child2</h1>
</div>;
});
const Child3 = forwardRef((props, ref) => {
return <div className="child3">
<h1>Child3</h1>
</div>;
});
const Child4 = forwardRef((props, ref) => {
return <div className="child4">
<h1>Child4</h1>
</div>;
});
const Child5 = forwardRef((props, ref) => {
return <div className="child5">
<h1>Child5</h1>
</div>;
});
function Home() {
let section = useRef();
let [currentSection, setCurrentSection] = useState(0);
useEffect(() => {
window.addEventListener("keypress", (event) => {
if(event.key === "d") {
// scroll right (play animation forwards by one step)
} else if(event.key === "a") {
// scroll left (play animation backwards by one step)
}
});
});
return <div ref={section} className="section">
<Child1></Child1>
<Child2></Child2>
<Child3></Child3>
<Child4></Child4>
<Child5></Child5>
</div>
}
export default Home;
The problem is that the animation plays all at the beginning and I cannot figure out a way to play it only when the keyboard event is triggered, if the event.key is an "a" then the elements should scroll to the left otherwise if the event.key is a "d" then the elements should scroll to the right.
Here is the link to the CodeSandbox.
As I understood you want to make parent scrollable only with pressing keys "a" and "d". I think I have found a solution which would work for you.
My solution:
Removing keyframes and separating it into 5 different classes. So here is CSS file:
.section1 {
transform: translateX(0);
}
.section2 {
transform: translateX(-100vw);
}
.section3 {
transform: translateX(-200vw);
}
.section4 {
transform: translateX(-300vw);
}
.section5 {
transform: translateX(-400vw);
}
.section {
display: flex;
flex-direction: row;
font-family: Arial, sans-serif;
font-weight: bold;
transition: 1s;
width: 100vw;
height: 100vh;
animation-name: scroll;
animation-duration: 1s;
}
.child1 {
background-color: #c0392b;
flex: none;
width: 100vw;
height: 100vh;
}
.child2 {
background-color: #e67e22;
flex: none;
width: 100vw;
height: 100vh;
}
.child3 {
background-color: #27ae60;
flex: none;
width: 100vw;
height: 100vh;
}
.child4 {
background-color: #2980b9;
flex: none;
width: 100vw;
height: 100vh;
}
.child5 {
background-color: #8e44ad;
flex: none;
width: 100vw;
height: 100vh;
}
Now I added some JS behaviour to appropriately 'scroll' left or right. I divided moving left or right into functions, and adding numberOfSections as constant. Here is JS file:
import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
import { Link } from "react-router-dom";
import "./Home.css";
const Child1 = forwardRef((props, ref) => {
return (
<div className="child1">
<h1>Child1</h1>
</div>
);
});
const Child2 = forwardRef((props, ref) => {
return (
<div className="child2">
<h1>Child2</h1>
</div>
);
});
const Child3 = forwardRef((props, ref) => {
return (
<div className="child3">
<h1>Child3</h1>
</div>
);
});
const Child4 = forwardRef((props, ref) => {
return (
<div className="child4">
<h1>Child4</h1>
</div>
);
});
const Child5 = forwardRef((props, ref) => {
return (
<div className="child5">
<h1>Child5</h1>
</div>
);
});
const numberOfSections = 5;
function Home() {
const section = useRef();
const [currentSection, setCurrentSection] = useState(1);
const moveLeft = () => {
section.current.classList.remove(`section${currentSection}`);
section.current.classList.add(`section${currentSection - 1}`);
setCurrentSection((prevSection) => prevSection - 1);
};
const moveRight = () => {
section.current.classList.remove(`section${currentSection}`);
section.current.classList.add(`section${currentSection + 1}`);
setCurrentSection((prevSection) => prevSection + 1);
};
const changeSection = (event) => {
if (event.key === "d") {
if (currentSection < numberOfSections) {
moveRight();
}
} else if (event.key === "a") {
if (currentSection > 1) {
moveLeft();
}
}
};
useEffect(() => {
window.addEventListener("keypress", changeSection);
return () => window.removeEventListener("keypress", changeSection);
}, [currentSection]);
return (
<div ref={section} className="section">
<Child1></Child1>
<Child2></Child2>
<Child3></Child3>
<Child4></Child4>
<Child5></Child5>
</div>
);
}
export default Home;
I have tested it and it works.
By the way, my suggestions is to wrap some of these functions into useCallback if you plan to expand Home component. And I suggest to use some of npm packages compatible with React for directly manipulating CSS within JS, such as styled-components, which in this case would make it a lot easier to create Carousel, becouse now in order to create more Child components, you need to make new CSS classes.
But this above fixes the given problem. I hope I helped you :)
this solution is tightly cuppled to your example but from my perspective, you could make it more generic.
I didn't use CSS animation instead I used the scrollTo method to achieve scroll behavior.
codesandbox link
Home.jsx
import { forwardRef, useEffect, useRef, useState } from "react";
import "./styles.css";
// I not using forwardRef and ref at all
const Child1 = forwardRef((props, ref) => {
return (
<div className="child1">
<h1>Child1</h1>
</div>
);
});
const Child2 = forwardRef((props, ref) => {
return (
<div className="child2">
<h1>Child2</h1>
</div>
);
});
const Child3 = forwardRef((props, ref) => {
return (
<div className="child3">
<h1>Child3</h1>
</div>
);
});
const Child4 = forwardRef((props, ref) => {
return (
<div className="child4">
<h1>Child4</h1>
</div>
);
});
const Child5 = forwardRef((props, ref) => {
return (
<div className="child5">
<h1>Child5</h1>
</div>
);
});
function App() {
let section = useRef();
let [currentSection, setCurrentSection] = useState(0);
useEffect(() => {
const handler = (event) => {
if (event.key === "d") {
if (currentSection === 4) return;
setCurrentSection((prev) => prev + 1);
} else if (event.key === "a") {
if (currentSection === 0) return;
setCurrentSection((prev) => prev - 1);
}
};
window.addEventListener("keypress", handler);
// you should clean up you EventListener
return () => {
window.removeEventListener("keypress", handler);
};
}, [currentSection]);
useEffect(() => {
window.scrollTo({
behavior: "smooth",
left: window.innerWidth * currentSection,
top: 0,
});
}, [currentSection]);
return (
<div ref={section} className="section">
<Child1></Child1>
<Child2></Child2>
<Child3></Child3>
<Child4></Child4>
<Child5></Child5>
</div>
);
}
export default App;
Home.css
.section {
display: flex;
flex-direction: row;
font-family: Arial, sans-serif;
font-weight: bold;
transition: 1s;
width: 100vw;
height: 100vh;
}
.child1 {
background-color: #c0392b;
flex: none;
width: 100vw;
height: 100vh;
}
.child2 {
background-color: #e67e22;
flex: none;
width: 100vw;
height: 100vh;
}
.child3 {
background-color: #27ae60;
flex: none;
width: 100vw;
height: 100vh;
}
.child4 {
background-color: #2980b9;
flex: none;
width: 100vw;
height: 100vh;
}
.child5 {
background-color: #8e44ad;
flex: none;
width: 100vw;
height: 100vh;
}
I'm pretty new to express and socket.io and I'm trying to achieve a little website:
What is it supposed to do:
You can connect to the website and enter a username
You have to select a column where you want to write (it's stored in var column)
Once on the page with the four column, you can see your username at the top of your column and start doing things there.
The other users see you in the correct column.
What it is not doing:
Actually the three points above are working quite well, my issue is with the last point :
The other users see you in the correct column.
My code is somehow not displaying every user in the correct column, in fact, it's displaying them in the same column as you are
Here is the code
$(document).ready(function () {
var socket = io();
var username = prompt("premier utilisateur : ", "nom");
var column = prompt("colonne ", "1,2,3 ou 4");
var gdhb = "";
socket.emit("new user entered his name");
socket.emit("nomUser", username);
if (column === "1") { column = ".one"; gdhb = ".dir1" }
if (column === "2") { column = ".two"; gdhb = ".dir2" }
if (column === "3") { column = ".three"; gdhb = ".dir3" }
if (column === "4") { column = ".four"; gdhb = ".dir4" }
socket.emit("user chose a column");
socket.emit("columnUser", column);
$(column).append($("<p class='username'>" + username + "</p>"))
$(document.body).click(function (b) {
var verbes = [
"appuie",
"bouscule",
"pousse"
];
var adverbes = [
"puis",
"ensuite",
"pour finir",
"alors"
];
var verbe = verbes[Math.floor(Math.random() * verbes.length)];
var adverbe = adverbes[Math.floor(Math.random() * adverbes.length)];
var verbadv = verbe + " " + adverbe;
console.log(verbadv);
socket.emit("verbadverbe");
socket.emit("verbadv", verbadv);
var div = $("<div />", {
"class": "document"
})
.css({
"left": b.pageX + 'px',
"top": b.pageY + 'px'
})
.append($("<p>" + verbadv + "</p>"))
.appendTo(column);
});
$(document.body).contextmenu(function (rc) {
var div = $("<div />", {
"class": "document"
})
.css({
"left": rc.pageX + 'px',
"top": rc.pageY + 'px'
})
.append($("<p>recule</p>"))
.appendTo(column);
});
var direction = "";
var oldx = 0;
var oldy = 0;
mousemovemethod = function (e) {
if (e.pageX > oldx && e.pageY == oldy) {
direction = "gauche";
}
else if (e.pageX == oldx && e.pageY > oldy) {
direction = "bas";
}
else if (e.pageX == oldx && e.pageY < oldy) {
direction = "haut";
}
else if (e.pageX < oldx && e.pageY == oldy) {
direction = "droite";
}
$(gdhb).append($("<p class='direction' id='direction'>" + direction + "</p>"))
$(".direction").prev().remove();
oldx = e.pageX;
oldy = e.pageY;
}
document.addEventListener('mousemove', mousemovemethod);
socket.on("columnUser", function (column) {
socket.on("nomUser", function (username) {
$(column).append($("<p class='username'>" + username + "</p>"));
socket.on("verbadv", function (verbadv) {
var div = $("<div />", {
"class": "document"
})
.append($("<p>" + verbadv + "</p>"))
.appendTo(column);
});
});
});
});
and the index.js :
const path = require('path');
const http = require('http');
const express = require('express');
const socketio = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(express.static(path.join(__dirname, 'public')));
io.on('connection', (socket) => {
console.log('Nouvel utilisateur')
socket.on("nomUser", (username) => {
console.log(username);
io.emit("nomUser", username);
});
socket.on("verbadv", (verbadv) => {
console.log(verbadv);
io.emit("verbadv", verbadv);
});
socket.on("columnUser", (column) => {
console.log(column);
io.emit("columnUser", column);
});
});
server.listen(3000, () => {
console.log('listen on 3000');
})
Also if it's needed to understand better, here is the css
body {
font-family: sans-serif;
font-size: 1.3rem;
margin: 0;
background-color: DarkSlateGray;
}
.wrapper {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 0px;
grid-auto-rows: minmax(100vh, auto);
height: 100vh;
}
.one,
.two,
.three,
.four {
-ms-overflow-style: none; /* Internet Explorer 10+ */
scrollbar-width: none; /* Firefox */
position: relative;
overflow: scroll;
height: 100%;
background-color: tan;
}
.one {
grid-column: 1 / 2;
}
.two {
grid-column: 2 / 3;
}
.three {
grid-column: 3 / 4;
}
.four {
grid-column: 4 / 4;
}
.one::-webkit-scrollbar,
.two::-webkit-scrollbar,
.three::-webkit-scrollbar,
.four::-webkit-scrollbar {
display: none; /* Safari and Chrome */
}
.note {
text-align: center;
width: 100px;
height: 30px;
}
.note p{
filter: drop-shadow(0 0 0.75rem black);
}
.document{
text-align: center;
}
.document p{
padding: 0;
margin: 0;
}
.username{
text-align: center;
padding: 0;
margin: 0;
}
.direction{
position: fixed;
bottom : 0;
width: 25vw;
text-align: center;
}
Thanks a lot for the precious help.
i've solved your problem with sockets. See at my solution.
client.js
function columnIndexIsValid(index, columnsQuantity) {
return index >= 0 && index <= columnsQuantity;
}
function fullNameIsValid(fullName) {
return typeof fullName === 'string' && fullName.length > 2;
}
function reloadPage() {
window.location.reload();
}
function rand(min, max) {
return Math.floor(min + Math.random() * (max - 1 - min));
}
function getRandomColour(colours = []) {
const colour = colours[rand(0, colours.length)];
return `#${colour}`;
}
function getUserHtml(user) {
return `<div class="column__users-list__item" data-item-id="${user.id}">${user.fullName}</div>`;
}
function getDrawnUsersNodes() {
return $('.column__item');
}
function canIRenderUsers(usersQuantity) {
const $renderedUsersQuantity = getDrawnUsersNodes().length;
return $renderedUsersQuantity < usersQuantity;
}
function renderUserHtmlToNode($node, html) {
$node.html($node.html() + html);
}
function getColumnUsersList(columnNode) {
const $column = $(columnNode);
return $column.find('.column__users-list');
}
function removeDrawnUserById(userId) {
$(`[data-item-id=${userId}]`).remove();
}
class DrawnUsers {
constructor() {
this.users = new Map();
}
getUserById(id) {
return this.users.get(id);
}
add(id, state) {
this.users.set(id, state);
}
removeById(id) {
this.users.delete(id);
}
exists(id) {
return this.users.has(id);
}
}
class Storage {
static setItem(key, value) {
localStorage.setItem(key, value);
}
static getItem(key) {
return localStorage.getItem(key) || null;
}
}
function generateUserId() {
return `user-${rand(rand(0, 10000), rand(20000, 50000))}`;
}
class UserState {
constructor() {
this.state = {};
}
get() {
return {
data: this.state,
};
}
set fullName(fullName) {
this.state.fullName = fullName;
}
get fullName() {
return this.state.fullName;
}
set id(id) {
this.state.id = id;
}
get id() {
return this.state.id;
}
set columnIndex(columnIndex) {
this.state.columnIndex = columnIndex - 1;
}
get columnIndex() {
return this.state.columnIndex;
}
}
$(document).ready(function () {
const drawnUsers = new DrawnUsers();
const colours = ['F2994A', 'F2C94C', '6FCF97', '2F80ED', '56CCF2', 'DFA2F5'];
const $columns = $('.column');
const $container = $('.container');
const userState = new UserState();
$columns.each(function () {
const $self = $(this);
$self.css({ 'background-color': getRandomColour(colours) });
});
userState.fullName = prompt('Type your fullName');
userState.columnIndex = +prompt('Type your column number');
if (
!fullNameIsValid(userState.fullName) ||
!columnIndexIsValid(userState.columnIndex, $columns.length)
) {
return reloadPage();
}
$container.addClass('active');
const socket = io('ws://localhost:3000');
socket.on('connect', () => {
const generatedUserId = generateUserId();
userState.id = Storage.getItem('userId') || generatedUserId;
Storage.setItem('userId', userState.id);
socket.emit('connected', userState.get());
socket.emit('addUser', userState.get());
socket.on('updateCurrentUsers', ({ data }) => {
const { users } = data;
if (!users || !canIRenderUsers(users.length)) {
return;
}
users.forEach((user) => {
const $column = $columns[user.columnIndex];
if ($column) {
if (!drawnUsers.exists(user.id)) {
drawnUsers.add(user.id);
renderUserHtmlToNode(
getColumnUsersList($column),
getUserHtml(user)
);
}
}
});
});
socket.on('newUser', ({ data }) => {
console.log('[debug] newUser: ', data);
const $column = $columns[data.columnIndex];
if (!$column) {
return;
}
if (drawnUsers.exists(data.id)) {
drawnUsers.removeById(data.id);
removeDrawnUserById(data.id);
} else {
drawnUsers.add(data.id);
renderUserHtmlToNode(getColumnUsersList($column), getUserHtml(data));
}
});
socket.on('disconnect', () => {
socket.open();
});
});
});
server.js
const path = require('path');
const http = require('http');
const express = require('express');
const socketIo = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
app.use(express.static(path.join(__dirname, 'public')));
io.on('connection', (socket) => {
console.log('[debug] Client was connected successfully to server');
socket.on('connected', (user) => {
console.log('connected user data', user.data);
socket.data = user.data;
const users = Array.from(io.sockets.sockets.values()).map(
({ data }) => data
);
console.log('users', users);
socket.emit('updateCurrentUsers', {
data: {
users,
},
});
});
socket.on('addUser', ({ data }) => {
socket.data.columnIndex = data.columnIndex;
socket.broadcast.emit('newUser', {
data,
});
const users = Array.from(io.sockets.sockets.values()).map(
({ data }) => data
);
socket.broadcast.emit('updateCurrentUsers', {
data: {
users,
},
});
});
});
server.listen(3000, () => {
console.log('listen on 3000');
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="./index.css">
<title>Users Columns</title>
</head>
<body>
<div class="container">
<div class="column">
<span class="column__index">#1</span>
<div class="column__users-list"></div>
</div>
<div class="column">
<span class="column__index">#2</span>
<div class="column__users-list"></div>
</div>
<div class="column">
<span class="column__index">#3</span>
<div class="column__users-list"></div>
</div>
<div class="column">
<span class="column__index">#4</span>
<div class="column__users-list"></div>
</div>
</div>
<script
src="https://code.jquery.com/jquery-3.5.1.js"
integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc="
crossorigin="anonymous"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.0.1/socket.io.js"
integrity="sha512-vGcPDqyonHb0c11UofnOKdSAt5zYRpKI4ow+v6hat4i96b7nHSn8PQyk0sT5L9RECyksp+SztCPP6bqeeGaRKg=="
crossorigin="anonymous"></script>
<script src="./client.js"></script>
</body>
</html>
index.css
html, body {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
}
.container {
display: none;
}
.container.active {
display: flex;
flex-direction: row;
height: 100vh;
}
.container .column {
width: calc(100% / 4);
text-align: center;
padding: 20px 0;
}
.container .column .column__users-list {
display: flex;
flex-direction: column;
}
.container .column .column__index {
color: #FFF;
font-size: 36px;
letter-spacing: 8px;
}
hey) Let's look on that case more attentive, you have created a variable what contains a column index. right? This variable does store inside var variable. That is global variable for your script. I think you need to store each user column info inside each socket. If you don't know, each socket contains some info inside itself like id and other what can be used in your project if you'd like. But for this case you need to do so:
Ask new user what column he would like to choose.
Write to his socket data/info his column index.
When you do broadcast (It's when you send an object of data to each socket in room all each socket at all) just take this column index and draw this user in correct position. But make sure what your var column has a correct value. I can advice you to use const/let in javascript :)
I created a traffic light using vue.js, but it doesn't work. It should display the colours (red, yellow and green) according to the time duration. Is there a problem that I've missed?
CSS
#screen {
width: 450px;
height: 740px;
background: #222;
border-radius: 12px;
margin: auto;
padding: 23px;
}
.light {
width: 230px;
height: 270px;
display: inline-block;
opacity: 0.2;
border-radius: 100%;
transition: opacity 10s;
margin-bottom: 12px;
}
.active {
opacity: 1;
}
.red {
background: red;
}
.yellow {
background: yellow;
}
.green {
background: green;
}
My HTMl
I've created the divs.
<div id="screen">
<div id="light red" :class="{active: now=='red'}"></div>
<div id="light yellow" :class="{active: now=='yellow'}"></div>
<div id="light green" :class="{active: now=='green'}"></div>
</div>
and this is vue.js
It seems like everything on it's place and console doesn't send any error.
But I still can't understand, why it isn't working?
class State {
constructor(name, dur, next){
this.name = name;
this.dur = dur;
this.next = next;
}
}
class Constroller {
trigger(state, callback){
callback(state);
setTimeout(()=>{
this.trigger(state.next, callback);
}, state.dur * 10)
}
}
var app = new Vue({
el: '#screen',
data:{
now: 'red'
},
mounted(){
var constroller = new Constroller();
var red = new State('red', 2);
var yellowRed = new State('yellow', 1);
var yellowGreen = new State('yellow', 1);
var green = new State('green', 3);
red.next = yellowRed;
yellowR.next = green;
green.next = yellowGreen;
yellowG.next = red;
constroller.trigger(red, (state)=>{
this.now = state.name;
});
}
})
Am I missing smth? Should I rewrite my function?
Well there are several things wrong here:
<div id="light red" :class="{active: now=='red'}"></div>
<div id="light yellow" :class="{active: now=='yellow'}"></div>
<div id="light green" :class="{active: now=='green'}"></div>
Should be class instead of id.
var red = new State('red', 2);
var yellowRed = new State('yellow', 1);
var yellowGreen = new State('yellow', 1);
var green = new State('green', 3);
red.next = yellowRed;
yellowR.next = green;
green.next = yellowGreen;
yellowG.next = red;
You named yellowRed and yellowGreen but then used yellowR and YellowG
Anyway, I made a quick fiddle here https://jsfiddle.net/Lo50j4rw/ that you can check out, I also tweak some duration stuff.
Also, at least in my country the light goes green after red :D
Your main problem is that you used id where you should have used class for your HTML light elements. You also had weird choices for timing: each light had a very short duration, but the transition was 10 seconds.
Fixing those, you get this:
class State {
constructor(name, dur, next) {
this.name = name;
this.dur = dur;
this.next = next;
}
}
class Constroller {
trigger(state, callback) {
callback(state);
setTimeout(() => {
this.trigger(state.next, callback);
}, state.dur * 1000)
}
}
new Vue({
el: '#screen',
data: {
now: 'red'
},
mounted() {
var constroller = new Constroller();
var red = new State('red', 2);
var yellowRed = new State('yellow', 1);
var yellowGreen = new State('yellow', 1);
var green = new State('green', 3);
red.next = yellowRed;
yellowRed.next = green;
green.next = yellowGreen;
yellowGreen.next = red;
constroller.trigger(red, (state) => {
this.now = state.name;
});
}
})
#screen {
width: 450px;
height: 740px;
background: #222;
border-radius: 12px;
margin: auto;
padding: 23px;
}
.light {
width: 230px;
height: 270px;
display: inline-block;
opacity: 0.2;
border-radius: 100%;
transition: opacity 0.3s;
margin-bottom: 12px;
background-color: white;
}
.active {
opacity: 1;
}
.red {
background: red;
}
.yellow {
background: yellow;
}
.green {
background: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="screen">
<div class="light red" :class="{active: now=='red'}"></div>
<div class="light yellow" :class="{active: now=='yellow'}"></div>
<div class="light green" :class="{active: now=='green'}"></div>
</div>
So I am not using any CSS framework like bootstrap to get responsiveness out of the box which is why I am having trouble making responsive layout.
Please see jsbin
I essentially what to auto-resize colorful boxes based on browser window size eg that should shrink or grow automatically based on window size. Colorful boxes inside their parent should always be in horizontal row but should be able to adjust their width and height like this example.
I tried using flex-wrap: nowrap; but it didn't do the trick :(
Please note that colorful boxes are using position:absolute with parent's position being relative. I am also adding left css property to these boxes via JavaScript to change their position for the sake of sliding animation.
function Carousel(options) {
options = options || {};
// options vars
let speed = options.speed || 1; // animation speed in seconds
let width = options.width || 200;
let height = options.height || 100;
let space = options.space || 30;
// other vars
let container = document.querySelector('.carousel-container .carousel');
let slides = container.querySelectorAll('.carousel-item');
let curSlide = null;
let prevSlide = null;
let nextSlide = null;
if (areSlidesPresent()) {
setup();
}
// functions //
function setup() {
// we assume first slide to be current one as per UI requirements
//slides[0].classList.add("current");
curSlide = slides[0];
// we assume second slide to be next as per UI requirements
nextSlide = slides[1];
// we assume last slide to be prev as per UI requirements
prevSlide = slides[slides.length - 1];
// position elements horizontally
positionSlides();
}
function areSlidesPresent() {
return slides.length > 0;
}
this.getCurrentSlide = function() {
return curSlide;
}
this.getNextSlide = function() {
return nextSlide;
}
this.getPreviousSlide = function() {
return prevSlide;
}
this.setNextSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(nextSlide);
// middle ones
for (let i = 2; i < slides.length; i++) {
allSlides.push(slides[i]);
}
allSlides.push(curSlide);
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
this.setPreviousSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(prevSlide);
allSlides.push(curSlide);
// middle ones
for (let i = 1; i < slides.length - 1; i++) {
allSlides.push(slides[i]);
}
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
function positionSlides() {
curSlide.style.marginLeft = '0px';
for (let i = 0; i < slides.length; i++) {
slides[i].querySelector('.carousel-content').style.width = (width) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height) + 'px';
let elementWidth = getStyle(nextSlide, 'width');
if (i === 0) {
slides[i].style.zIndex = -10;
//slides[i].style.opacity = '1';
slides[i].querySelector('.carousel-content').style.width = (width + 50) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height + 50) + 'px';
} else {
slides[i].style.zIndex = 0;
//slides[i].style.opacity = '0.7';
}
if (i > 0) {
slides[i].style.marginLeft = (space * 2) + 'px';
elementWidth = parseInt(elementWidth, 10) + space;
}
slides[i].style.transition = speed + 's';
slides[i].style.left = (elementWidth * i) + 'px';
}
}
function getStyle(el, prop) {
return window.getComputedStyle(el, null).getPropertyValue(prop)
.replace('px', '')
.replace('em', '');
}
}
// utility
function log(text) {
console.log(text);
}
var options = {
speed: 1, // animation speed
width: 250, // slide width
height: 150, // slide height
space: 25 // space in px between slides
};
var carousel = new Carousel(options);
function selectCurrent() {
log(carousel.getCurrentSlide());
}
function selectNext() {
carousel.setNextSlide();
}
function selectPrev() {
carousel.setPreviousSlide();
}
.carousel-container {
width: auto;
height: auto;
margin: 25px;
display: flex;
align-items: center;
justify-content: center;
}
.carousel {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.carousel .carousel-item {
position: absolute;
transition: transform .5s ease-in-out;
color: #fff;
margin-left: 10px;
-webkit-box-reflect: below 10px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(70%, transparent), to(rgba(255, 255, 255, 0.2)));
}
.carousel .carousel-item:first-child .carousel-content {
opacity: 1;
}
.carousel .carousel-item .carousel-title {
font-size: 24px;
text-align: center;
}
.carousel .carousel-item .carousel-content {
font-size: 18px;
font-weight: bold;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
}
/* temp css below */
body {
background: #2C374A;
padding-top: 150px;
}
.navigation {
display: flex;
align-items: center;
justify-content: center;
margin-top: 150px;
}
.button {
color: #444;
padding: 10px;
width: 60px;
cursor: pointer;
background: #CCC;
text-align: center;
font-weight: bold;
border-radius: 5px;
border-top: 1px solid #FFF;
box-shadow: 0 5px 0 #999;
transition: box-shadow 0.1s, top 0.1s;
margin: 10px;
}
.button:hover,
.button:hover {
color: #000;
}
.button:active,
.button:active {
top: 104px;
box-shadow: 0 1px 0 #999;
}
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<div class="carousel-title">Make a Call</div>
<div class="carousel-content" style="background:#0E6DE8;border:10px solid #78B1FA">Slide One</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Message</div>
<div class="carousel-content" style="background:#D90080;border:10px solid #E357A9">Slide Two</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Picture</div>
<div class="carousel-content" style="background:#FEC601;border:10px solid #FFDD64">Slide Three</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Video</div>
<div class="carousel-content" style="background:#3DB365;border:10px solid #90E0AB">Slide Four</div>
</div>
</div>
</div>
<div class="navigation">
<div class="button" onclick="selectNext()">Next</div>
<div class="button" onclick="selectCurrent()">Select</div>
<div class="button" onclick="selectPrev()">Prev</div>
</div>
Problem here was:
Width was hard-coded in your JS, so if width is in px it can't be responsive.
By applying position:absolute to you carousel-item, it forced the children to get out of the box.
What I did:
Got rid of the static width and other functionalities related to width from your JS
Removed position:absolute from carousel-item
Let me know if this is what you are expecting.
function Carousel(options) {
options = options || {};
// options vars
let speed = options.speed || 1; // animation speed in seconds
// let width = options.width || 100;
let height = options.height || 100;
let space = options.space || 30;
// other vars
let container = document.querySelector('.carousel-container .carousel');
let slides = container.querySelectorAll('.carousel-item');
let curSlide = null;
let prevSlide = null;
let nextSlide = null;
if (areSlidesPresent()) {
setup();
}
// functions //
function setup() {
// we assume first slide to be current one as per UI requirements
//slides[0].classList.add("current");
curSlide = slides[0];
// we assume second slide to be next as per UI requirements
nextSlide = slides[1];
// we assume last slide to be prev as per UI requirements
prevSlide = slides[slides.length - 1];
// position elements horizontally
positionSlides();
}
function areSlidesPresent() {
return slides.length > 0;
}
this.getCurrentSlide = function() {
return curSlide;
}
this.getNextSlide = function() {
return nextSlide;
}
this.getPreviousSlide = function() {
return prevSlide;
}
this.setNextSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(nextSlide);
// middle ones
for (let i = 2; i < slides.length; i++) {
allSlides.push(slides[i]);
}
allSlides.push(curSlide);
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
this.setPreviousSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(prevSlide);
allSlides.push(curSlide);
// middle ones
for (let i = 1; i < slides.length - 1; i++) {
allSlides.push(slides[i]);
}
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
function positionSlides() {
curSlide.style.marginLeft = '0px';
for (let i = 0; i < slides.length; i++) {
// slides[i].querySelector('.carousel-content').style.width = (width) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height) + 'px';
let elementWidth = getStyle(nextSlide, 'width');
if (i === 0) {
slides[i].style.zIndex = -10;
//slides[i].style.opacity = '1';
// slides[i].querySelector('.carousel-content').style.width = (width + 50) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height + 50) + 'px';
} else {
slides[i].style.zIndex = 0;
//slides[i].style.opacity = '0.7';
}
if (i > 0) {
slides[i].style.marginLeft = (space * 2) + 'px';
// elementWidth = parseInt(elementWidth, 10) + space;
}
slides[i].style.transition = speed + 's';
// slides[i].style.left = (elementWidth * i) + 'px';
}
}
function getStyle(el, prop) {
return window.getComputedStyle(el, null).getPropertyValue(prop)
.replace('px', '')
.replace('em', '');
}
}
// utility
function log(text) {
console.log(text);
}
var options = {
speed: 1, // animation speed
width: 250, // slide width
height: 150, // slide height
space: 25 // space in px between slides
};
var carousel = new Carousel(options);
function selectCurrent() {
log(carousel.getCurrentSlide());
}
function selectNext() {
carousel.setNextSlide();
}
function selectPrev() {
carousel.setPreviousSlide();
}
.carousel-container {
height: auto;
margin: 25px;
display: flex;
}
.carousel {
flex: 1;
height: 100%;
width: 100vh;
/* overflow:hidden; */
display: flex;
align-items: center;
justify-content: center;
}
.carousel .carousel-item {
transition: transform .5s ease-in-out;
color: #fff;
flex: 1;
margin-left: 10px;
-webkit-box-reflect: below 10px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(70%, transparent), to(rgba(255, 255, 255, 0.2)));
}
.carousel .carousel-item:first-child .carousel-content {
opacity: 1;
}
.carousel .carousel-item .carousel-title {
font-size: 24px;
text-align: center;
}
.carousel .carousel-item .carousel-content {
font-size: 18px;
font-weight: bold;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
}
/* temp css below */
body {
background: #2C374A;
}
.navigation {
display: flex;
align-items: center;
justify-content: center;
}
.button {
color: #444;
padding: 10px;
width: 60px;
cursor: pointer;
background: #CCC;
text-align: center;
font-weight: bold;
border-radius: 5px;
border-top: 1px solid #FFF;
box-shadow: 0 5px 0 #999;
transition: box-shadow 0.1s, top 0.1s;
margin: 10px;
}
.button:hover,
.button:hover {
color: #000;
}
.button:active,
.button:active {
box-shadow: 0 1px 0 #999;
}
<div class="navigation">
<div class="button" onclick="selectNext()">Next</div>
<div class="button" onclick="selectCurrent()">Select</div>
<div class="button" onclick="selectPrev()">Prev</div>
</div>
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<div class="carousel-title">Make a Call</div>
<div class="carousel-content" style="background:#0E6DE8;border:10px solid #78B1FA">Slide One</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Message</div>
<div class="carousel-content" style="background:#D90080;border:10px solid #E357A9">Slide Two</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Picture</div>
<div class="carousel-content" style="background:#FEC601;border:10px solid #FFDD64">Slide Three</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Video</div>
<div class="carousel-content" style="background:#3DB365;border:10px solid #90E0AB">Slide Four</div>
</div>
</div>
</div>