Border-image conic-gradient example does not work in React - javascript

I am working to redo this Codepen in React Typescript. I found it in the blogpost here
Simple way - creating React App and adding into css file it works perfect.
No I tried the way with styled components and it seems I am missing something as it does not work yet?
App.tsx
import React from "react";
import "./App.css";
import styled from "styled-components";
/* Animate when Houdini is available */
const Houdini = styled.div`
width: 400px;
height: 300px;
border-radius: 10px;
padding: 2rem;
margin: auto;
display: grid;
place-content: center;
text-align: center;
font-size: 1.5em;
--border-size: 0.3rem;
border: var(--border-size) solid transparent;
/* Paint an image in the border */
border-image: conic-gradient(
from var(--angle),
#d53e33 0deg 90deg,
#fbb300 90deg 180deg,
#377af5 180deg 270deg,
#399953 270deg 360deg
)
1 stretch;
background: rgb(255 255 255 / var(--opacity));
#supports (background: paint(houdini)) {
#property --opacity {
syntax: "<number>";
initial-value: 0.5;
inherits: false;
}
#property --angle {
syntax: "<angle>";
initial-value: 0deg;
inherits: false;
}
#keyframes opacityChange {
to {
--opacity: 1;
}
}
#keyframes rotate {
to {
--angle: 360deg;
}
}
.rainbow {
animation: rotate 4s linear infinite, opacityChange 3s infinite alternate;
}
/* Hide the warning */
.warning {
display: none;
}
}
`;
function App() {
return (
<>
<Houdini>
<p>
This demo uses a real border with <code>border-image</code>, a
background, and finally Houdini to animate.
</p>
</Houdini>
<div>
<p>
⚠️ Your browser does not support{" "}
<a href="https://web.dev/css-individual-transform-properties/">
#property
</a>{" "}
so the animation won’t work
<br />
Please use Chrome.
</p>
</div>
</>
);
}
export default App;
And a slight variation the working Codepen
And the change in my App.tsx the rest is the same like above - same issue, I used styled components and the effect does not show.
const Houdini = styled.div`
.
.
.
--border-size: 0.3rem;
border: var(--border-size) dotted transparent;
background-image: linear-gradient(
to right,
rgb(255 255 255 / var(--opacity)),
rgb(255 255 255 / var(--opacity))
),
conic-gradient(
from var(--angle),
#d53e33 0deg 90deg,
#fbb300 90deg 180deg,
#377af5 180deg 270deg,
#399953 270deg 360deg
);
background-origin: border-box;
background-clip: padding-box, border-box;
.
.
.
`;

The culprit is the nested CSS rule that launches the animation:
.rainbow {
animation: rotate 4s linear infinite, opacityChange 3s infinite alternate;
}
...which, in the CodePen sample, targets the element with the border:
<div class="rainbow">
<p>This demo uses a real border with <code>border-image</code>,
a background, and finally Houdini to animate.</p>
</div>
But once converted to styled-components, the <div> with the "rainbow" class name was replaced by the <Houdini> styled React component. Hence its class name is no longer "rainbow", but generated by styled-components.
<Houdini> // styled-components replaces it by something like "<div class="sc-bczRLJ kCseJt">"
<p>
This demo uses a real border with <code>border-image</code>, a
background, and finally Houdini to animate.
</p>
</Houdini>
In order to achieve the same effect (i.e. preparation then a nested rule to launch the animation, applied on the same class name), we can simply use the & (ampersand) identifier, that styled-components replaces by the generated class name (SASS/SCSS technique):
https://styled-components.com/docs/basics#pseudoelements-pseudoselectors-and-nesting
& a single ampersand refers to all instances of the component; it is used for applying broad overrides
& /*.rainbow*/ {
animation: rotate 4s linear infinite, opacityChange 3s infinite alternate;
}
...and now the animation works!
Note: do not forget to also define your initial CSS variables (e.g. in the global CSS file):
:root {
--angle: 45deg;
--opacity: 0.5;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
Demo on CodeSandbox: https://codesandbox.io/s/winter-dream-ej7yec?file=/src/App.tsx:1069-1176

Related

add transitional effect (fade-in) from small image to large image on button click

Noob here.
I've written a javascript which will trade/swap out a small image for a larger (both wider and taller) image.
The SMALLER IMAGE (the default) appears on load and is part of the brief/condensed view. When we CLICK ON THE HEADLINE, we open the larger/detailed view and the short descriptions are replaced by more in-depth/longer text descriptions.
I am unfamiliar with js, but I've managed to get myself to this point which everything I want to happen is happening. However, looking at the result, it feels a little jarring, hence i'd like to ease the speed of the transformation - as the depth of the window is largely determined by transition/replacement of the larger image, I would like to if it is possible, transform/transition/animate the replace of the small image to the large on taking maybe a second to do so?
I am looking for help in achieving. The end goal being that the the replacements don't appear quite so fast so as not to be so jarring.
I would like to avoid jQuery if possible.
function sw_switchDETAILS() {
// transoform content
// changing div class => allows hide/show of specific content
const element = document.getElementById("description00");
if (element.className == "showTHIS") {
element.className = "hideTHIS";
} else {
element.className = "showTHIS";
}
}
function pictureChange() {
// transoform image
if (document.getElementById("image00").src == "<?=$square;?>") {
document.getElementById("image00").src = "<?=$profile;?>";
} else {
document.getElementById("image00").src = "<?=$square;?>";
}
}
.wp-100 {
width: 100%;
}
.wx-200 {
width: 200px;
}
.wx-50 {
width: 50px;
}
.wx-60 {
width: 60px;
}
.wx-80 {
width: 80px;
}
.wx-100 {
width: 100px;
}
.mwx-200 {
max-width: 200px;
}
.showTHIS {
/* border:4px solid lightgoldenrodyellow; */
-webkit-transition: all 1s linear;
-moz-transition: all 1s linear;
-o-transition: all 1s linear;
transition: all 1s linear;
}
.hideTHIS {
/* border:4px solid lightblue; */
-webkit-transition: all 1s linear;
-moz-transition: all 1s linear;
-o-transition: all 1s linear;
transition: all 1s linear;
}
/* hide extended content => Full Description, Participants, Location */
div#description00.hideTHIS p#hd-description.contentHIDE,
div#description00.hideTHIS p#hd-participants.contentHIDE,
div#description00.hideTHIS p#hd-location.contentHIDE {
display: none;
overflow: hidden;
}
/* show small img => as we are in teaser / short / minimal content mode */
div#image00.hideTHIS img.largeIMG {
display: none;
overflow: hidden;
}
/* show breif/basic teaser content => breif description
/* hide breif as we know are showing full description */
div#description00.showTHIS p#sw-breif {
display: none;
overflow: hidden;
}
/* hide small img - use larger image as we have available white space */
div#image00.showTHIS img.smallIMG {
display: none;
overflow: hidden
}
.btn_toggleTITLE {
writing-mode: horizontal-tb !important;
font-weight: bold;
font-size: 18px;
font-family: 'ff-bscompMED';
color: #981e21;
letter-spacing: normal;
word-spacing: normal;
line-height: normal;
text-align: left;
background-color: transparent;
margin: 0 0 .3em 0;
padding: 0px;
border-width: 0px;
border-style: outset;
border-color: transparent;
border-image: initial;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="mSIDES-8px mb-8px " style="border:4px solid #dedede!important;">
<table id="" class="">
<tr>
<td class="va-top pa-8px pr-none">
<img id="image00" src="http://placekitten.com/80/80">
</td>
<td class="va-top wp-100 pa-8px">
<p id="" class="mb-4px">
<button id="btn00" onclick="sw_switchDETAILS(); pictureChange()" class="btn_toggleTITLE ta-justify fs-20px ff-bscompMED">STORY / ARTICAL TITLE HERE</button>
<sup class="fs-14px">x | x | x</sup>
go 2 thread <i class="fas fa-rocket"></i>
</p>
<div id="description00" class="hideTHIS">
<p id="sw-breif" class="wp-100 ta-justify mb-4px"><strong class="c-redDARK">BRIEF:</strong> Breif description visible if extended desc hidden; 128 characters or less</p>
<!-- this content should be hidden on initial load, revealed with mouse click on title -->
<p id="hd-description" class="wp-100 ta-justify mb-4px contentHIDE"><strong class="c-redDARK">DESCRIPTION:</strong> Extended descrip hidden if breif desc visible; Upto 1000 characters; That's great. You did the hardest part. You took the jump, you didn't know where you were gonna come down. And that's it. That's
those little brave baby steps we gotta take. To try and become whole again, try and find purpose. I went in the ice in '45 right after I met the love of my life. Woke up 70 years later. You gotta move on. Gotta to move on. The world is in
our hands. It's left to us guys, and we got to do something with it. Otherwise... Thanos should have killed all of us.</p>
<!-- this content should be visible on initial load, hidden on mouse click of title -->
<p id="sw-order" class="wp-100 ta-justify mb-4px contentHIDE"><strong class="c-redDARK">ORDER:</strong> Breif description visible if extended desc hidden; 128 characters or less</p>
<p id="hd-participants" class="wp-100 ta-justify mb-4px contentHIDE"><strong class="c-redDARK">PARTICIPANTS:</strong> Extended descrip hidden if breif desc visible; Upto 1000 characters; </p>
<!-- this content should be hidden on initial load, revealed with mouse click on title -->
<p id="hd-location" class="wp-100 ta-justify mb-4px contentHIDE"><strong class="c-redDARK">LOCATIONS:</strong> Extended descrip hidden if breif desc visible; Upto 1000 characters; </p>
</div>
</td>
</tr>
</table>
</div>
<?php
# st test variables
$profile = 'http://placekitten.com/200/500';
$square = 'http://placekitten.com/80/80';
?>

Make so that div changes color for a brief time when clicked

I've got a setup where I'm using divs as buttons, and when they're clicked they add to ingredients to my burger.
JS:
<div id="ingredientBox">
<Ingredient
ref="ingredient"
v-for="item in currentIngredients"
v-on:increment="addToBurger(item)"
:item="item"
:lang="lang"
:ui-labels="uiLabels"
:key="item.ingredient_id">
</Ingredient>
</div>
With CSS:
.ingredient {
border: 1px solid #f5f5f28a;
padding: 0.8em;
width: 23vh;
height: 19vh;
background-color: green;
border-radius: 25px;
margin: 10px;
text-align: center;
}
I now want the div to react visually when clicked (maybe change color for like 0.2 seconds or something. I've looked around and only find info on how to change color permanently, is there a simple way of changing the color for just a brief moment?
You can use CSS keyframe animation to pull this off:
#keyframes animate-burger-button {
0% {
background: red;
}
50% {
background: yellow;
}
100% {
background: green;
}
}
#ingredientBox:active {
animation: animate-burger-button 0.8s forwards;
}
I would also add another note to try and use a button instead of a div, make accessibility a lot easier.
You could do something like
#ingredientBox:active {
color: red;
}
You could use setTimeout to add a class to the button and then remove it.
code:
buttonTrigger() {
element.classList.add('somesyle'); // add colour changing class to element
setTimeout(() => {
element.classList.remove('somestyle'); //remove the class after 0.2 seconds
}, 200)
}
EDIT
I was going to also suggest using CSS keyframes but #AlexanderKaran already suggested it. That is a good option too.

Progress- percentage bar for webpage

I am trying to make a loading bar but with percentage. I mean imagine when it’s 50% it will show that number also it will fill until half. If it’s 75% it will go up until 3/4.
I am trying to do this with HTML,CSS and JS. So far I made up everything but at the same time number will increase (by clicking button) and pattern will fill up according to percentage? This part is challenging me.
Can you lead me way if it’s possible? Or even if there is example so I can learn?
UPDATE CODE BELOW
<script>
var i = 0;
function buttonClick5() {
i += 5;
document.getElementById('demo').innerHTML = i + "€";
}
function percentage(per) {
return (100 / 100) * per;
}
</script>
<div class="textContainer">
<p class="perc" id="here"></p>
<script>
document.getElementById('here').textContent = percentage(10) + "%";
</script>
<h2 id="demo">0€</h2>
</div>
and css part
.textContainer {
margin-top:-10%;
margin-left: 30%;
height: auto;
text-align: center;
}
.textContainer h2 {
margin-left: -40%;
font-size: 500px;
color: rgba(225, 225, 225, .1);
background-image: url(color3.jpg);
background-repeat:repeat;
-webkit-background-clip: text;
animation: animateMid 15s linear infinite;
}
#keyframes animateMid {
0% {
background-position: left 0px top 0px;
}
1% {
background-position: left 1200px top 0px;
}
}
basically with those I can just make pattern moving as background of the percentage but always at 100 per cent.
Use your percentage variable as a width of the div that shows progress. Add click even listener to your button that will update that percentage variable and width of the div. If you make a callback function that updates both at the same it would be easy for you. Share your code here so we can help further.

React: Stop a styled-components animation from running when the component is first mounted

I have a styled-component that receives props to determine what animation to use. This is controlling an arrow icon that when active rotates 'open' and when inactive remains 'closed'.
Here is what the styled-component and two keyframes animations look like:
const rotate_down = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(90deg);
}
`;
const rotate_up = keyframes`
from {
transform: rotate(90deg);
}
to {
transform: rotate(0deg);
}
`;
const OpenUserSettings = styled.div`
cursor: pointer;
width: 20px;
height: 20px;
transition: 0.3s;
animation: ${props => (props.rotate ? rotate_down : rotate_up)} 0.5s ease
forwards;
margin-top: 2px;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
& > img {
width: 5px;
}
`
The passed in rotate prop is a boolean value that is toggled via an onClick handler in the React component:
<OpenUserSettings
rotate={arrowDown}
onClick={() => {
this.setState(prevState => ({
arrowDown: !prevState.arrowDown
}));
}}
>
<img src={OpenArrow} alt="menu drop down arrow" />
</OpenUserSettings>
This works, and when the arrow is clicked, rotate prop is passed into OpenUserSettings and it successfully toggles between the rotate_up and rotate_down keyframes.
Now my problem is that when the React component first mounts, the arrowDown default is set to false meaning that the rotate prop is going to be false. This causes the styled-component to set the animation to rotate_up the first time mounting. I figured this would be hard to visualize so check out this to see what I am describing:
You can see when the page is refreshed the rotate_up animation is firing very quickly. I need the arrow to stay closed, but I do not need the rotate_up animation to fire when first loaded to close it. Is this a situation for something like react-transition-group where I can control the initial enter or is it something that can be handled with logic?
My first contribution to stack overflow, how exciting!
To assure the animation does not render on mounting, initialise your state with something else as true/false, I remember setting my state to null initially.
In your styled component you can pass null / true / false as a prop and define a function outside your component that performs checks on the null / true / false. (I used TypeScript, so don't copy the types)
function showAnimationOrNot(status: boolean | null) {
if (status === true) {
return `animation: nameAnimationIn 1s ease forwards;`
} else if (status === false) {
return `animation: nameAnimationOut 1s ease-out forwards;`
} else if (status === null) {
return ""
}
}
In your styled component simply add a line:
${(props: PropsDisplay) => show(props.show)};
This is how I did it, I am sure there are more elegant ways, but it did the trick of preforming the animation on mounting.
In return if somebody knows how to get styled component syntax highlighting inside the return of functions like the one I used here (from the example above):
return `animation: comeout 1s ease-out forwards;`
Do let me know! looking for that! Thank you and good luck!
Keep on animating! :)
I think you'll have an easier time with a CSS transform/transition here, rather than an animation. Here's the code:
const rotateDeg = props.rotate ? '90deg' : '0deg';
const OpenUserSettings = styled.div`
cursor: pointer;
width: 20px;
height: 20px;
transition: all 0.3s ease;
transform: rotate(${rotateDeg});
margin-top: 2px;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
& > img {
width: 5px;
}
`
I didn't quite understand what position you wanted the arrow in, so you might need to flop 0deg and 90deg and I think it might actually be -90deg, but hopefully this helps.
Edit
Your question was actually how to stop an animation. You can pause a css animation with animation-play-state. In this case you could have had the component mount with animation-play-state: paused; and then added an additional prop to change that to running on first click, but that seems unnecessarily complicated IMO.
More on animation-play-state.

Need some help in creating a visual representation of device orientation using JS

Basically, I am wanting to create a visual representation of the device orientation values alpha, beta, and gamma. So far I have managed to display the values in plain text using innerHTML, but I want to create a series of "bars" for each value. I drew a very crude drawing of what I had in mind:
Basically, I want the bars to move in relation to changes in the alpha, beta and gamma values. This is how my code looks now.
<!DOCTYPE HTML>
<html>
<body>
<p>Alpha: <span id="alpha"></span></p>
<p>Beta: <span id="beta"></span></p>
<p>Gamma: <span id="gamma"></span></p>
<canvas id="myCanvas" width="400" height="400"></canvas>
<script>
// Listen for device orientation event
window.ondeviceorientation = function(eventData)
{
// Show alpha, beta and gamma values
document.getElementById('alpha').innerHTML = Math.round(eventData.alpha);
document.getElementById('beta').innerHTML = Math.round(eventData.beta);
document.getElementById('gamma').innerHTML = Math.round(eventData.gamma);
}
</script>
</body>
</html>
I highly doubt I can do this using inner HTML because I think I would need to use CSS styling. This makes me think that the canvas might work, but I have trouble initializing it using the ondeviceorientation. I would appreciate any help in accomplishing this.
You don't need a canvas. You can do this just by altering the width of your spans along with using CSS to set the colors.
Firstly make sure your spans have a CSS property of display : inline-block or display : blockor else changing the width will do nothing. Alternatively you can make them divs instead of spans. Also make sure it has a height property set such as 30px.
Next you can use css or inline-styles to set the background-color property for alpha, beta, and gamma. Then simply change the Element.style.width property (in px) based on the device orientation using javascript.
Something you might want to consider deeply is what you want the size of the bars to represent and their precise behavior. That design decision is up to you, so I won't explain in excruciating detail how the following code works, but basically I size them relative to the range of the values. I turn the value of alpha, beta, and gamma respectively into a percentage of their total range and then multiply that by the max width I would like for the bars.
I grabbed the ranges from here: https://developer.mozilla.org/en-US/docs/Web/API/Detecting_device_orientation
The general formula is for values in range [a, b] and a maximum bar width of max_w, and alpha, beta, or gamma value of value, the calculated width of the bar is:
width = max_w * ( ( -a + value ) / (b - a) )
And don't forget to add "px" to the end.
// Listen for device orientation event
window.ondeviceorientation = function(eventData)
{
let maxWidth = 200;
// Show alpha, beta and gamma values
document.getElementById('alpha').style.width = Math.round(maxWidth * eventData.alpha / 360) + "px";
document.getElementById('beta').style.width = Math.round(maxWidth * (180 + eventData.beta) / 360) + "px";
document.getElementById('gamma').style.width = Math.round(maxWidth * (90 + eventData.gamma) / 180) + "px";
}
p span{
height : 30px;
display : inline-block;
}
#alpha{
background-color : green;
}
#beta {
background-color : yellow;
}
#gamma {
background-color : purple
}
<p> Alpha: <span id="alpha"> </span> </p>
<p> Beta: <span id="beta"> </span> </p>
<p> Gamma: <span id="gamma"> </span></p>
The previous poster, Khauri McClain, posted a good suggestion for a static representation of your orientation values. If by "move" you mean, however, an animation (and hence refer to canvas), then you can still do it without canvas, but instead using CSS keyframes. Here is quick example.
html,
body {
height: 100%;
}
body {
background-color: #f5f7f9;
color: #6c6c6c;
margin: 0;
position: relative;
}
.container {
width: 30em;
margin: 2em;
}
.label {
float: left;
width: 5em;
height: 2em;
}
.orientation {
float: right;
background-color: #e5e9eb;
height: 2em;
position: relative;
width: 24em;
}
.alpha {
animation-duration: 3s;
animation-name: alpha-anim;
animation-fill-mode: forwards;
background-color: #ff2d55;
height: 100%;
position: relative;
}
.beta {
animation-duration: 3s;
animation-name: beta-anim;
animation-fill-mode: forwards;
background-color: #4cd964;
height: 100%;
position: relative;
}
.gamma {
animation-duration: 3s;
animation-name: gamma-anim;
animation-fill-mode: forwards;
background-color: #007aff;
height: 100%;
position: relative;
}
#keyframes alpha-anim {
0% {
width: 0;
}
100% {
width: 14em;
}
}
#keyframes beta-anim {
0% {
width: 0;
}
100% {
width: 3em;
}
}
#keyframes gamma-anim {
0% {
width: 0;
}
100% {
width: 20em;
}
}
<div class="container">
<div class="label">Alpha:</div>
<div class="orientation">
<div class="alpha"></div>
</div>
</div>
<div class="container">
<div class="label">Beta:</div>
<div class="orientation">
<div class="beta"></div>
</div>
</div>
<div class="container">
<div class="label">Gamma:</div>
<div class="orientation">
<div class="gamma"></div>
</div>
</div>

Categories