SVG animation controlling by Aurelia - javascript

I have a problem with an animation controlled with Aurelia.
app.html
<path
style="fill:none;stroke:#000000;stroke-width:3"
d="M 1.6443859,34.308976 C 0.37273852,24.831561 7.0248286,16.117725 16.502244,14.846077 25.979658,13.574431 34.693495,20.226519 35.965142,29.703934 37.236789,39.181349 30.5847,47.895186 21.107285,49.166833 15.541877,49.913581 10.23978,47.927929 6.5533998,44.242297"
>
<animateTransform
attributeName="transform" attributeType="XML" type="rotate" from.bind="fromCW"
to.bind="toCW" dur.bind="duration" fill="freeze"
/>
</path>
<button repeat.for="state of states" click.trigger="changeState({state})">${state}</button>
app.ts
states = ["opening", "closing"];
duration:string = "10s";
rotationPoint: string = " 18.9 32.05";
rightRotation = -95 + this.rotationPoint;
leftRotation= 130 + this.rotationPoint;
zero = 0 + this.rotationPoint;
state:string;
fromCW:string;
fromCCW:string;
toCW:string;
toCCW:string;
domeAnimate(): void{
switch (this.state){
case "opening":
this.fromCW = this.fromCCW = this.zero;
this.toCW= this.leftRotation;
this.toCCW = this.rightRotation;
break;
case "closing":
this.fromCW = this.leftRotation;
this.fromCCW = this.rightRotation;
this.toCW = this.toCCW = this.zero;
break;
}
}
changeState(id):void {
this.state = id.state;
this.domeAnimate();
}
Here is how does it look like
Right now the animation starts when the button is clicked. i.e: when I click the "Opening" button the opening animation starts, then switch to "Closing" and the closing animation is in the middle. When the animation is done I am not able to run it again.
What I'd like to do it here is to start the proper animation (opening or closing) when I click the button connected to it everytime I click it.
I don't want to use SVG.beginElement() here.
Thanks

The issue you got is because SVG animation via <animateTransform/> by default is run only once. Try adding an attribute repeatCount="indefinite", it will work as expected.
Here is a link to a working version https://stackblitz.com/edit/aurelia-javascript-p77wvh?file=src%2Fapp.js

Related

I want to make a random colorBox with colorcode [duplicate]

This question already has answers here:
Random color generator
(64 answers)
Closed 11 months ago.
thanks for coming here to help me.
I'm a very beginner of programmer. I want to make a js code about the random colorcode. ex)colorcode means #556549 like that.
My intention is
first, to make a array with random colorcode,
and second, to pick up the one colorcode and use that code to one part.
And other part has the another colorcode.
It means the code below, "???" needs your help..
<<<<. style="file:???" >>>>
here is my code,
<g
**id="strokes"
var colorBox = ["#D8FCBE", "#B6EECF", "#F9FDE1", "#FFE9EA", "#CCEDE4", "#A6D2E1", "#A8A6DB", "#D3BAE9", "#F3D0F5", "#FFE7F7"]
var randomColor = Math.floor(Math.random() * colorBox.length)
return randomColor
<path
style="file:"randomColor;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
id="path2"
<path
style="file:randomColor;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
id="path4"
<path
style="file:randomColor;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
id="path6"
style="fill:green;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
id="path8"
It is impossible to do this because HTML and CSS is not for dynamic things. You have to use JS to do this.
<g id="strokes">
<path id="path2" class="paths">
<path id="path4" class="paths">
<path id="path6" class="paths">
<path id="path8" class="paths">
</g>
window.addEventListener('load', () => {
document.querySelectorAll('.paths').forEach(elem => {
const colorBox = ["#D8FCBE", "#B6EECF", "#F9FDE1", "#FFE9EA", "#CCEDE4", "#A6D2E1", "#A8A6DB", "#D3BAE9", "#F3D0F5", "#FFE7F7"]
elem.style.fill = colorBox[Math.floor(Math.random() * colorBox.length)]
})
})

Firing off an event when clicking a specific button from within a forEach() loop

I am currently trying to set two different events on two different buttons, however, these buttons (h1's) are within a forEach() loop. Long story short, it goes like this: When clicking a button, the text will appear underneath it and disappear if clicked again (this is working perfectly so far). Now, I decided to add an svg with an animation, but for this I need to target the specific button since I need to apply different CSS depending on which button is clicked.
I can't seem to figure out how to set an event for one button since I've created a forEach() loop that calls the button and respective text from within the HTML template.
Is there anyone who can, from looking at these short code snippets, help me find a way to incorporate what I'm missing regarding the on click svg/animation?
NB: The JS code snippet below is currently firing off the animation that I would like to set for button 1 <h1 class="tourTitle> on both buttons.
<section id="tours">
<h1 class="whatWeOffer">What we offer</h1>
<template class="tourTemplate">
<div id="singleTourArea">
<h1 class="tourTitle"></h1>
<p class="tourText"></p>
</div>
</template>
<div id="tourArea"></div>
<svg id="boatSvg" class="show" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2110 1350">
<path d="M907.8.2c-31.9 80.3-71.4 157.6-117.9 230.4-29 45.5-61.7 91.7-67.7 145.3-11.9 106 83.4 200.2 78.1 306.7-12 18.7-42.3 11.3-57.6-4.8-15.3-16.1-24.1-38.3-42.1-51.3 22.5 14.2 53.5-8.1 58.2-34.2s-7.8-51.9-19.9-75.6c65.1 70.8 102 166.7 101.3 262.8-93.4-57-165.9-147.4-201.2-251 30.8 89.3 88.2 169.3 162.9 227 63 48.6 141.2 85.5 176.3 156.9 19.2 39.2 23.1 85.2 46.7 122 11.2 17.4 26.2 31.8 41.3 45.8 38.1 35.4 77.4 69.5 117.9 102.1 21.8 17.6 43.9 34.7 66 51.9 49.7 38.6 99.5 77.1 149.2 115.7" fill="none" stroke="#000" stroke-miterlimit="10"/>
</svg>
</section>
function showTours(tours) {
// 1. template clone
const tourTemplate = document.querySelector(".tourTemplate").content;
const tourArea = document.querySelector("#tourArea");
tours.forEach((oneTour) => {
const tourCopy = tourTemplate.cloneNode(true);
tourCopy.querySelector(".tourTitle").textContent = oneTour.title.rendered;
const tourText = tourCopy.querySelector(".tourText");
tourText.textContent = oneTour.description;
//Expand single tour
tourCopy.querySelector(".tourTitle").addEventListener("click", function(){
if (tourText.style.display === "block") {
tourText.style.display = "none";
document.querySelector("#boatSvg").classList.add("show");
document.querySelector("#singleTripArea:nth-of-type(5n)").classList.remove("flashAnimation");
document.querySelector("#singleTripArea:nth-of-type(3n)").classList.remove("flashAnimation");
} else {
tourText.style.display = "block";
document.querySelector("#boatSvg").classList.remove("show");
document.querySelector("#boatSvg").classList.add("boatAnimation");
document.querySelector("#singleTripArea:nth-of-type(5n)").classList.add("flashAnimation");
document.querySelector("#singleTripArea:nth-of-type(3n)").classList.add("flashAnimation");
}
})
tourArea.appendChild(tourCopy);
})
}
In your
tours.forEach((oneTour) => {
//A bunch of stuff
});
You are looping tours and on each iteration, you can refer the current element as oneTour.
So, if oneTour is having a tourTitle class, then, instead of
tourCopy.querySelector(".tourTitle").addEventListener("click", function(){
//A bunch of stuff
});
you can do this:
oneTour.addEventListener("click", function(){
//A bunch of stuff
});
Note that it's possible that I did not quite get what you want to happen with what. If that's the case, I would welcome some further clarification of the problem.
If I understand correctly, you want to add different events to different elements, and attach them inside a forEach loop. Maybe try something like this (basic example):
function event1 () {
console.log('This is the first button');
}
function event2 () {
console.log('This is the second button');
}
var events = [event1, event2];
tours.forEach((oneTour, i) => {
// Do some stuff
someElement.addEventListener('click', events[i]);
}
I'm essentially just defining the event handlers outside the loop, storing them in a list, and getting the index of the forEach alongside the value. I then use the index to access the correct function. I hope that is of some help. (Or perhaps I did not quite understand).

Apply style to element when click outside of directive attribute element in Angular

I have svg items that highlights their corresponding path when user click on them or hover them.
I'm using Directives to detect onEnter onLeave and Click events.
Its working fine when I hover ( I highlight ) then when I leave the element (I Play down )
Then When I click also the path is highlighted (
Actually what's happen is when I click elsewhere the Highlight stays.
What I can't do so is when I click on the rest of the screen I would like that the Highlight disappear.
Here's my directive logic
export class HighlightDirective {
constructor(private renderer:Renderer2 , private el: ElementRef) {}
clicked=false
#HostListener('mouseenter') onMouseEnter() {
this.changeOpacity(1);
}
#HostListener('mouseleave') onMouseLeave() {
if (!this.clicked) {
this.changeOpacity(0);
}
}
#HostListener('click') click() {
this.changeOpacity(1);
this.clicked = true;
}
changeOpacity(opacity: number) {
this.renderer.setStyle(this.el.nativeElement.nextSibling, 'opacity', opacity);
}
}
And here's a part of the SVG with the Highlight attribute tag
<g id="b1" transform="translate(-885 -562)">
<g id="b1select">
<ellipse id="Ellipse_1" data-name="Ellipse 1" cx="5.3" cy="5.3" rx="5.3" ry="5.3" transform="translate(1309.17 932.12)" fill="none" stroke="#b18802" stroke-width="1"/>
<text id="B1-2" data-name="B1" transform="translate(1310.67 939.62)" fill="#b18802" font-size="6" font-family="ArialMT, Arial"><tspan x="0" y="0">B1</tspan></text>
</g>
<rect appHighlight (click)="openDialog($event)" id="container" width="12" height="11" transform="translate(1308.67 931.62)" fill="rgba(255,255,255,0)"/>
<path id="b1-3" data-name="b1" d="M1093.763,1595.658v-82.417s5-14.987-18.452-16.644-40.9,2.386-54.093-11.537-132.873-159.193-132.873-159.193-6.456-10.249-24.986-14.661-9.858-17.907-4.728-25.235,39.039-47.23,39.039-47.23" transform="translate(208.67 -656.38)" fill="none" stroke="#efcf2f" stroke-linecap="round" stroke-width="2"/>
</g>
Here's an example on stackBlitz
document.removeEventListener('mousedown', this.scroll);
What I have used is that whenever mousedown event fires removeListner remove scroll event. Which we added before using addEventListner() . this.scroll is your defined event to do what you want to do.

React JSX Dynamic SVG Translate Error

I'm new to React and trying to do a dynamic Svg Transition where I translate the Position of a Group according to the current Component State. For some reason I get the Error Message:
DOMPropertyOperations.js:139 Error: <g> attribute transform: Expected
'(', "translate:(100 100)".
Here is the render function:
render() {
let turns = Route.Leg.map((turn) => <circle cx={turn.to.location.latitude} cy={turn.to.location.longitude} r="5"></circle>)
let legs = Route.Leg.map((leg) => <line x1={leg.from.location.latitude} y1={leg.from.location.longitude} x2={leg.to.location.latitude} y2={leg.to.location.longitude} stroke="black"></line>)
let move = "translate:(" + this.state.pos.location.latitude + " " + this.state.pos.location.longitude + ")";
return (
<div>
<svg width="800px" height="800px">
<circle cx="400" cy="400" r="10"></circle>
<g transform={move}>
{turns}
{legs}
</g>
</svg>
</div>
);
}
The lines and circles are drawn correctly, and when I log the "move" variable and looks correct every time the dom updates. When I hardcode the translate it also works. Does someone have an idea what is wrong here, or am I just missing something? Cheers in advance
As says in the error Expected (',...
// Look down, there is no ":" character in css syntax. And check for ","
let move = "translate(" + this.state.pos.location.latitude + "," + this.state.pos.location.longitude + ")";
That's because of you are using wrong syntax. You should use translate(... not translate:(....
Also you should comma seperate values inside translate

Using a random color for an SVG being exported as a React Component

I have three SVGs that I'm using as React components. The three SVGs are currently being saved as JavaScript files. They are being used like this:
import React from 'react';
import PictureA from '../components/SVGs/PictureA.js'
import PictureB from '../components/SVGs/PictureB.js'
import PictureC from '../components/SVGs/PictureC.js'
const IndexPage = () => (
<div className="mainArea">
<PictureB />
<PictureA />
<PictureC />
</div>
)
export default IndexPage
The above part works fine. Within, for example, PictureA.js, I would like to have something like this happen:
import React from 'react';
export default function PictureA(props) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
svg width = "12rem"
height = "auto"
version = "1.1"
viewBox = "-10 -110 100 250"
preserveAspectRatio="xMinYMax slice">
<g transform="translate(-2.202509394435669,-16.15250659108221)"
fill = "FILL THIS WITH A RANDOM COLOR"
fill-opacity = "0.90087" >
<path
d="very long path" />
</g>
</svg>
);
}
I have an array of pre-selected colors, and it's no problem to select from that array. The problem I'm having is in inserting a color once selected into:
fill = "FILL THIS WITH A RANDOM COLOR"
I've been trying to save the randomly selected color as a variable and populate it directly inside the tag. After doing some research, I get the feeling that's the wrong approach. But if there is a way to make that work, that would be optimal. If that isn't the right approach, I would still really appreciate any help making the randomization work within the PictureA.js file. It would be best to have the SVGs self-contained, if that's possible.
Thanks very much!
Have you tried passing a generated color as a prop?
fill = { props.color }
And then call it as
<PictureA color={ chooseRandomColor() } />
[EDIT] If you want this to be self-contained, you can also just inline the call to the random color generator:
return (
<svg
xmlns="http://www.w3.org/2000/svg"
svg width = "12rem"
height = "auto"
version = "1.1"
viewBox = "-10 -110 100 250"
preserveAspectRatio="xMinYMax slice">
<g transform="translate(-2.202509394435669,-16.15250659108221)"
fill = { chooseRandomColor() }
fill-opacity = "0.90087" >
<path ... />
)
}
You can inline JavaScript by surrounding it with { } tags in React JSX templates. See here in the react docs for an example.

Categories