Anymation changes color to black when it should keep the gradient - css

As a case study I was trying to replicate what hyperplexed brings up at the end of the video.
https://www.youtube.com/watch?v=owpaafxvkjU&ab_channel=Hyperplexed
i tried to apply the gradient to the words, however, by doing so the color of the word changes to back (even though the word split effect still happens)
I have tried to do it on hover, tried to change the colors, but still happens.
const enhance = (id) => {
const element = document.getElementById('split');
const text = element.innerText.split('');
element.innerText = '';
text.forEach((letter) => {
const span = document.createElement('span');
span.className = 'letter';
span.innerText = letter;
element.appendChild(span);
});
};
const enhance1 = (id) => {
const element = document.getElementById('split1');
const text = element.innerText.split('');
element.innerText = '';
text.forEach((letter) => {
const span = document.createElement('span');
span.className = 'letter';
span.innerText = letter;
element.appendChild(span);
});
};
const enhance2 = (id) => {
const element = document.getElementById('split2');
const text = element.innerText.split('');
element.innerText = '';
text.forEach((letter) => {
const span = document.createElement('span');
span.className = 'letter';
span.innerText = letter;
element.appendChild(span);
});
};
const enhance3 = (id) => {
const element = document.getElementById('split3');
const text = element.innerText.split('');
element.innerText = '';
text.forEach((letter) => {
const span = document.createElement('span');
span.className = 'letter';
span.innerText = letter;
element.appendChild(span);
});
};
enhance('split');
enhance('split1');
enhance2('split2');
enhance3('split3');
body {
background-color : #000;
margin : 0;
display : flex;
flex-direction : column;
align-items : center;
justify-content : center;
height : 100vh;
}
.line {
display : flex;
justify-content : space-between;
}
p,
a {
color : #fff;
font-size : 5vw;
font-family : 'Rubik', sans-serif;
margin : 0rem;
text-transform : uppercase;
}
a {
text-decoration : none;
}
#text:has(.fancy:hover) .word:not(.fancy:hover) {
opacity : 0.4;
}
.fancy > .letter {
display : inline-block;
transition : transform 350ms ease;
}
.fancy:hover > .letter {
transition-duration: 800ms;
}
.fancy:hover > .letter:nth-child(1) { transform : translate(-80%, 60%) rotate( 8deg); }
.fancy:hover > .letter:nth-child(2) { transform : translate(0%, 8%) rotate(-6deg); }
.fancy:hover > .letter:nth-child(3) { transform : translate(-10%, 60%) rotate(-6deg); }
.fancy:hover > .letter:nth-child(4) { transform : translate(0%, 8%) rotate(-8deg); }
.fancy:hover > .letter:nth-child(5) { transform : translate(0%, 60%) rotate(-3deg); }
.fancy:hover > .letter:nth-child(6) { transform : translate(0%, 20%) rotate(-3deg); }
.fancy:hover > .letter:nth-child(7) { transform : translate(0%, 80%) rotate(-5deg); }
#keyframes background-pan
{
from { background-position: 0% center; }
to { background-position: -200% center; }
}
p,
a,
.letter > .magic {
animation : background-pan 15s linear infinite;
background : linear-gradient(to right,#ecf2ff,#e3dffd,#e5d1fa,#fff4d2,#ecf2ff);
-webkit-background-clip : text;
-webkit-text-fill-color : transparent;
}
<div class="container" id="text">
<div class="line magic">
Home
</div>
<div class="line magic">
About
</div>
<div class="line magic">
Shop
</div>
<div class="line magic">
Contact
</div>
</div>
If anyone knows what it's causing the color to change to black please let me know, or point me towards some documentation so I can have a read through.

My issue was that I should be calling the gradient inside the .fancy>letter instead of getting the p,a and letter.
it now works as intended

Related

How to set ScrollToTop Button to be active on viewport height?

At the moment i am using hardcoded height point to trigger visible ScrollToTop Button.
i would love to get solution to be triggered when passing viewport height.
const { scrollDirection } = useScrollDirection()
const { scrollPosition } = useScrollPosition()
const [isVisible, setIsVisible] = useState(false)
const toggleVisible = () => {
if (scrollPosition === 0) {
setIsVisible(false)
}
**if (scrollPosition > 800) {
setIsVisible(true)
} else if (scrollPosition <= 799) {
setIsVisible(false)
}**
}
const scrollToTop = () => {
window.scrollTo({
top: 0,
behavior: "smooth",
})
}
window.addEventListener("scroll", toggleVisible)
you can use window.innerHeight
const toggleVisible = () => {
const viewportHeight = window.innerHeight;
if (scrollPosition === 0) {
setIsVisible(false)
}
**if (scrollPosition > viewportHeight) {
setIsVisible(true)
} else if (scrollPosition <= viewportHeight) {
setIsVisible(false)
}**
}
You can do this by using Intersection Observer (IO)
First you create an element that is just below the viewport initially. And whenever this element comes into view, show the button.
This requires one dummy element which you observe, for the demo I set the html element to position: relative for it to work. Maybe you can use a different element structure, based on your html. Important thing is that you have one element you can observe and trigger the element depending on when it comes into view.
let options = {
rootMargin: '0px',
threshold: 0.1 // when at least 10% of the element is visible we show the button
}
const callback = (entries, observer) => {
const btn = document.querySelector('#scroll-top');
entries.forEach(entry => {
if (entry.intersectionRatio > 0.1) {
// if we are past our 0.1 threshold we show the button
btn.classList.add('visible')
} else {
// otherwise we hide the button
btn.classList.remove('visible')
}
});
};
const observer = new IntersectionObserver(callback, options);
const target = document.querySelector('#button-trigger');
observer.observe(target);
.dummy-viewport {
min-height: 400vh;
}
html {
position: relative;
}
#button-trigger {
position: absolute;
top: 100vh;
left: 10px;
height: calc(100% - 100vh);
/* for demo purposes, don't show the element on the finished site*/
width: 2rem;
outline: 1px solid rebeccapurple;
writing-mode: vertical-rl;
text-orientation: mixed;
}
p {
padding: 0;
margin: 0;
}
#scroll-top {
position: fixed;
bottom: 40px;
right: 10px;
opacity: 0;
transition: .5s opacity;
}
#scroll-top.visible {
opacity: 1
}
<div class="dummy-viewport">
<p> Scroll down ↓ </p>
<button id="scroll-top" type="button"> Scroll to top </button>
</div>
<div id="button-trigger">
<p> When I am visible, I show the button </p>
</div>

How to make the color of a div change permanently after it has been clicked in react.js

This is the grid that I have
Once a square has been clicked, the color changes to red. - This is done using active class
I would like the change to the background color of the div tag to remain permanent after the square has been clicked.
Code
Board.js
{board.map((row, rowIdx) => (
<div key={rowIdx} className="row">
{row.map((cell, cellIdx) => (
<div key={cellIdx} className="cell"></div>
))}
</div>
))}
Board.css
.row {
height: 30px;
margin: 10px;
transition: transform 0.1s;
}
.cell:hover {
transform: scale(1.4);
}
.cell {
width: 30px;
height: 30px;
outline: 1px solid rgb(134, 154, 189);
display: inline-block;
background-color: seagreen;
margin: 5px;
transition: transform 0.2s;
}
.row :active {
background-color: red;
}
.cell :active { // Does not do anything
background-color: blue;
}
Once another square is clicked, the previosly clicked one does not remain active, you can do this by adding another class with your desired style, and using state to track the squares that have been clicked.
const [boardIndeces, setBoardIndeces] = useState(initArray(board));
const initArray = arr => {
let rows = [];
for (let i = 0; i < arr.length; i++) {
let row = [];
const currBoard = arr[i];
for (let z = 0; z < currBoard.length; z++) {
row[z] = false;
}
rows[i] = row;
}
return rows;
};
const onCellClick = (rowIdx, cellIdx) => {
if (!(boardIndeces[rowIdx] && boardIndeces[rowIdx][cellIdx])) {
boardIndeces[rowIdx][cellIdx] = true;
setBoardIndeces([...boardIndeces]);
}
};
{
board.map((row, rowIdx) => (
<div key={rowIdx}>
{row.map((cell, cellIdx) => (
<div
key={cellIdx}
className={
boardIndeces[rowIdx].includes(cellIdx)
? 'your_active_class'
: 'your_inactive_class'
}
onClick={() => onCellClick(rowIdx, cellIdx)}
></div>
))}
</div>
));
}
You can use a checkbox if you don't want to use React
HTML
<input type="checkbox" id="color" name="color">
<label id="color-div" for="color">
<!-- the div you want to change color -->
</label>
CSS
#color {
display: none
}
#color:checked + #color-div { /* + in CSS is the sibling combinator */
background-color: #fffff /* your background color */
}
What you're basically doing is making an invisible checkbox and toggling it with the label, when it's toggled you do the changes to the CSS
Remember the CSS combinators only work in elements after the HTML element it's being applied to

How to generate a random number each time a component re-renders?

I want to use Less variables to generate a random number for conditionally class that gets added and removed each second. Here is the sample code of reactjs file
function App() {
const [date , setDate] = useState('')
const [circleBoolean, setCircleBoolean] = useState(false);
const [random_number, setRandom_number] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
doThings()
}, 1000);
return () => clearInterval(interval);
}, []);
function doThings() {
setCircleBoolean((prev)=>{
return !prev});
setDate(moment().format('MMMM Do YYYY, h:mm:ss a')) ;
setRandom_number(Math.random() * 100);
console.log("cirlce boolean :" + circleBoolean)
}
return (
<div className="App">
<div className="bg">
<div style={{ top:`${random_number}%`, left:`${random_number}%` }} className={circleBoolean ? "circle" : ""} />
<div className="card">
<p className="card-info">{date}</p>
</div>
</div>
</div>
);
}
and here is the sample code from css file
#color: blue;
#random: (round(`Math.random()`)) ;
.circle {
position: absolute;
border: solid 4px #color;
border-radius: 50%;
height: #random;
width: 3em;
background-color: #color;
animation: circleSize 1s ease-in-out;
}
#keyframes circleSize {
0% {
transform: scale(0, 0);
}
50% {
transform: scale(1,1);
}
100%{
transform: scale(0,0);
}
}
The random number is generated only during the first render and is not changed during subsequent re-renders. How do I go about it ?
My advice is use a 'css-in-js' approach.
React only re-renders components that changed and your css is static right now. If you define your styles in a js file and pass your random number to this file every time your App's interval runs - you'll get your re-renders.

CSS animation in a column

I have many div stacked up in a column. When top div is destroyed lower divs will naturally come up and vice versa. I want that transition to be smooth.
How do I achieve this? Do I apply CSS animation class to each inserted div? I am confused. Pls advice. Thanks.
$add = document.getElementById('add');
$remove = document.getElementById('remove');
$parent = document.getElementById('parent');
let i = 0
$add.onclick = function() {
let el = document.createElement("DIV");
let text = document.createTextNode("Hello " + i);
el.appendChild(text);
$parent.insertBefore(el, $parent.firstElementChild);
i++;
}
remove.onclick = function() {
$parent.removeChild($parent.firstElementChild);
}
<button id="add" onclick="myFunction()">Add child on top</button>
<button id="remove" onclick="removeChlid()">remove child on top</button>
<div id="parent">
</div>
Animation can be achieved using css if we are adding element.
In case of removing we need to fisrt add class before removing to start animation and then remove element from DOM when animation is done. In example there is the same 500m delay, of course it can be done also checking animationEnd event in js.
$add = document.getElementById('add');
$remove = document.getElementById('remove');
$parent = document.getElementById('parent');
let i = 0
$add.onclick = function(){
let el = document.createElement("DIV");
el.className='test';
let text = document.createTextNode("Hello "+ i );
el.appendChild(text);
$parent.insertBefore(el, $parent.firstElementChild);
i++;
}
remove.onclick = function(){
$parent.firstElementChild.className += ' remove-animation';
setTimeout(() => {
$parent.removeChild($parent.firstElementChild);
}, 500)
}
.test {
max-height: auto;
animation: test-animation 0.5s;
transition: max-height 0.5s;
margin-bottom: 5px;
overflow: hidden;
}
.remove-animation {
animation: remove-animation 0.5s;
}
#keyframes test-animation {
0% { max-height: 0; }
100% { max-height: 25px }
}
#keyframes remove-animation {
0% { max-height: 25px; }
100% { max-height: 0 }
}
<button id="add" onclick="myFunction()">Add child on top</button>
<button id="remove" onclick="removeChlid()">remove child on top</button>
<div id="parent">
</div>

How to slide or hide a div with transition in react?

I am trying to display a div with the click of a button with slide effect. When something is clicked, it will toggle as shown or invisible with slide effect. I have achieved this so far by doing this.
class App extends Component {
constructor() {
super();
this.state = {
myclass: '',
}
this.divclicked = this.divclicked.bind(this);
}
divclicked() {
if (this.state.myclass === '') {
this.setState({
myclass: 'coolclass'
})
}
else {
this.setState({
myclass: '',
})
}
}
render() {
return (
<div className="App">
<div id="box" onClick={this.divclicked}>
</div>
<div id="seconddiv" className={this.state.myclass}>
<p>help help</p>
<p>help help</p>
<p>help help</p>
<p>help help</p>
<p>help help</p>
</div>
</div>
);
}
}
export default App;
And my CSS
#box {
height: 50px;
width: 50px;
background-color: red
}
#seconddiv.coolclass{
height:300px;
background: purple;
}
#seconddiv {
height: 0px;
transition: 0.5s;
overflow: hidden;
}
So here, when the red box with the id of "box" is clicked, I give the "seconddiv" a className and CSS takes care of hiding the div. The problem is that when I am giving the className coolclass, I do not want to use px but want to use percentage. So currently, it is going from 0px to 300px. But I want it to go from 0px to 100%; How do I achieve this. When I try to put the height of 100% in seconddiv, the slide animation doesn't work.
You need to set your initial height to 0%. You also need to give .App a height of 100% so that it stretches the full height of the window. In this example, I gave it a static height of 1200px, but that should be determined by the body.
class App extends React.Component {
constructor() {
super();
this.state = {
myclass: '',
}
this.divclicked = this.divclicked.bind(this);
}
divclicked() {
if (this.state.myclass === '') {
this.setState({
myclass: 'coolclass'
})
}
else {
this.setState({
myclass: '',
})
}
}
render() {
return (
<div className="App">
<div id="box" onClick={this.divclicked}>
</div>
<div id="seconddiv" className={this.state.myclass}>
<p>help help</p>
<p>help help</p>
<p>help help</p>
<p>help help</p>
<p>help help</p>
</div>
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('root'));
#box {
height: 50px;
width: 50px;
background-color: red
}
#seconddiv.coolclass{
max-height:100%;
background: purple;
}
#seconddiv {
max-height: 0%;
transition: 0.5s;
overflow: hidden;
}
.App {
height: 100%;
}
#root {
height: 1200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
You can just combine conditional rendering ( x && y ) with some scale animation
Example
const scaleAnimationIn = keyframes`
0% {
transform: scale(0, 1);
animation-timing-function: ease-out;
}
100% {
transform: scale(1, 1);
}
`
const QuestionHeaderPausedText = styled.div`
animation: ${scaleAnimationIn} 1s;
animation-delay: 7s;
animation-fill-mode: both;
`
In the return/render method just:
{isSomeConditionTrue && <QuestionHeaderPausedText>
My text
</QuestionHeaderPausedText>}
This example use Styled Components but the method (scaling) will work with any css solution.

Resources