how to make Angular animations div to show on mouse enter? - html

I'm trying to animate a div, when the mouse enters that specific div, apparently i couln't make it done.
Any ideas how this would work in my context?
This is the animations inside the TS file
animations: [
trigger('explainerAnim', [
transition('* => *', [
query('.card', style({ opacity: 0, transform: ' translateX(-400px'})),
query('.card', stagger('100ms', [
animate('1000ms 0.9s ease-out', style({opacity:1, transform: 'translateX(0)'}))
]))
])
])
]
And this is the div that i want to show based on mouse enter
<div [#explainerAnim] class="col-sm-12 text-center about-page">
<div class="card">
<div class="developer-photo"></div>
<div class="card-body">
<h2>Who`s this guy?</h2>
<p>
I'm a Full-Stack Developer working for AppyWay in London. <br />
I have a serious passion for implementing high quality web
applications <br />
using the Microsoft stack E.g .Net Core.
</p>
</div>
</div>
<div class="card developer-details">
<div class="card-header developer-header">
<h2>I`m Norbert Csibi</h2>
<h5>Full-Stack Developer</h5>
</div>
<div class="card-body">
<p>
I am passionate about building excellent software that improves
the lives of those around me.
</p>
<table class="table table-borderless">
<thead>
<tr>
<th scope="col"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">EMAIL</th>
<td>#gmail.com</td>
</tr>
<tr>
<th scope="row">PHONE</th>
<td>55255</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>

You can add a (mouseenter) and (mouseleave) directives on the div element that you want to trigger the animation.
<div (mouseenter)="startAnimation()" (mouseleave)="stopAnimation()">
</div>
Then you can use the angular animation states to add two states, one animated and the other without it. And set the two different states inside the startanimation() and stopAnimation() methods.
Here I let you an example of how to use the angular states:
In the TypeScript:
#Component({
selector: 'app-open-close',
animations: [
trigger('openClose', [
// ...
state('open', style({
height: '200px',
opacity: 1,
backgroundColor: 'yellow'
})),
state('closed', style({
height: '100px',
opacity: 0.5,
backgroundColor: 'green'
})),
transition('open => closed', [
animate('1s')
]),
transition('closed => open', [
animate('0.5s')
]),
]),
],
templateUrl: 'open-close.component.html',
styleUrls: ['open-close.component.css']
})
export class OpenCloseComponent {
isOpen = true;
toggle() {
this.isOpen = !this.isOpen;
}
}
In the HTML:
<div [#openClose]="isOpen ? 'open' : 'closed'" class="open-close-container">
<p>The box is now {{ isOpen ? 'Open' : 'Closed' }}!</p>
</div>

Since this question was high in my search ranking, and is not that old 😉, I think others might be helped with a 'full' example
You can position the diw with the.highlight class with relative/absolute positions, but I use css-grid in this example.
TLDR;
Hostlisteners are used to change the animation states
when mouse enters and leaves
#HostListener('mouseenter', ['$event'])
onOver(event: MouseEvent): void {
this.highlightAnimationState = this.highlightStates.mouseEnter;
// console.log('mouseenter');
// console.log('Enter: this.highlightAnimationState :>> ', this.highlightAnimationState );
}
Full example >>
this is a class used to represent a row, a race track, for a team, like this:
| Team Name | (dots representing percent of max until finished at 100%) | Team Info
it is used like this in the parent component (having an array of teams to display).
race-board.component.html
<main class="main-content">
<div *ngFor="let team of raceTeams">
<app-race-track [team]="team">
<!-- if you are not familiar with ng-content (see 'app-race-track' component)
it is simply used to output whatever is inside the tags here -->
<app-race-track-divider [colorClass]="dividerPositions.getColor(i)"></app-race-track-divider>
</app-race-track>
</div>
</main>
race-track.component.html
<div
class="race-track-row"
*ngIf="team; else loadingTemplate">
<div [#highlightTrigger]="highlightAnimationState" class="highlight">
<!-- note, this is used to create higlight effect on mouseover -->
</div>
<div class="divider">
<ng-content>
<!-- here a divider line is printed; if it should be. -->
</ng-content>
</div>
<div class="team-details">
<div class="team-name">
{{team?.Name | shorten | uppercase }}
</div>
</div>
<div class="race-track" #raceTrackContainer>
<ng-container *ngFor="let item of filledDots; let i = index">
<div class="percentage-dot" id="filled-dot-{{i}}">
<mat-icon class="mat-icon-scale-up mat-icon-color-dark-grey" [inline]="true">
lens
</mat-icon>
</div>
</ng-container>
</div>
<div class="team-info">
<img src="../assets/images/race-board/raceboard-team-image.png" />
</div>
</div>
<ng-template #loadingTemplate>
... Loading race team ...
</ng-template>
</div>
You can position the div with the .highlight class using relative/absolute positions, but I use css-grid and it looks like this
race-track.component.scss
#use 'src/app/race-board-app-module' as appStyles;
/* the container of the component */
.race-track-row {
position: relative;
margin-top: 0.4rem;
width: fit-content;
display: grid;
grid-template-columns: 200px auto 80px;
grid-template-rows: auto 3rem ;
row-gap: 0.3rem;
// column-gap: 15px;
/* note: use dots to leave an area blank */
grid-template-areas:
" . track-divider . "
" team-details race-track team-info ";
}
/*note, this is used to create highlight effect on mouseover */
.highlight{
width: 100%;
grid-row: 2/3;
grid-column: 1/4;
}
.divider {
grid-area: track-divider;
}
.race-track{
grid-area: race-track;
position: relative;
padding-left: 2rem;
padding-right: appStyles.$race-track-padding-right;
display: flex;
flex-wrap: nowrap;
}
/** name (perhaps also icon and percent later?) */
.team-details {
grid-area: team-details;
justify-self: right;
align-self: center;
margin-right: 0.5rem;
padding-left: 1rem;
}
.team-name{
font-weight: 500;
}
.percentage-dot {
// align-items: center;
padding-left: appStyles.$padding-dots-container;
align-self: center;
}
.team-info{
grid-area: team-info;
display: flex;
align-items: center;
justify-content: center;
align-self: center;
}
/* variable must be imported like this for scale and calc to function */
$icon-scale: appStyles.$icon-scale;
.mat-icon-scale-up {
transform: scale($icon-scale);
padding: appStyles.$padding-dot-icons;
}
race-track.component.ts
/* you probably don't need all of the imports here, but cpy paste from implentation so .. */
import { Component, Input, OnInit, OnDestroy, ViewChild, ElementRef, AfterViewChecked, Renderer2, HostListener } from '#angular/core';
import { trigger, state, style, transition, animate, keyframes } from '#angular/animations';
#Component({
selector: 'app-race-track',
templateUrl: './race-track.component.html',
styleUrls: ['./race-track.component.scss'],
animations: [
trigger('highlightTrigger', [
state('in', style({
opacity: 0.1,
backgroundColor: 'lightblue'
})),
state('out', style({
opacity: 0.0,
backgroundColor: 'lightblue'
})),
/*
this transitions has keyframes to controll the in-out speed with offset (time in point from in to out, ) you can also use
*/
transition('* <=> *', animate(50, keyframes([
style({
opacity: 0.03,
offset: 0.3
}),
style({
opacity: 0.05,
offset: 0.5
}),
style({
opacity: 0.1, /* this should, but don't have to, match the 'final' state (i.e. 'in') */
offset: 1
}),
])))
])
]
})
export class RaceTrackComponent implements OnInit, OnDestroy, AfterViewChecked {
//animation:
highlightAnimationState = "";
highlightStates = {
mouseEnter: 'in',
mouseLeave: 'out'
}
#Input() public team?: RaceBoardTeam;
/** dark icons; reprecenting team score as percent of max */
public filledDots?: number[];
constructor() { }
/** Animations: hostlisteners are used to change the animation states
* when mouse enters and leaves the race-track
*/
#HostListener('mouseenter', ['$event'])
onOver(event: MouseEvent): void {
this.highlightAnimationState = this.highlightStates.mouseEnter;
// console.log('mouseenter');
// console.log('Enter: this.highlightAnimationState :>> ', this.highlightAnimationState );
}
#HostListener('mouseleave', ['$event'])
onOut(event: MouseEvent): void {
this.highlightAnimationState = this.highlightStates.mouseLeave;
// console.log('mouseleave');
// console.log('Leave: this.highlightAnimationState :>> ', this.highlightAnimationState );
}
// other stuff omitted ....
}

Related

Is there a way to change the appeareance of an html text when hovering on the div that contains it?

I need to color and zoom the text when the cursor "approaches" the text (so basically when the mouse enters the area of the div surrounding the text). Right now i can make it work coloring the text only when i hover directly on it. I'll paste a snippet of the code.
HTML:
<div fxLayout="row wrap" class="max container">
<div fxFlex="100%" fxLayoutAlign="center">
<!--here there is an image-->
</div>
<div fxFlex="100%" class="centered-text" fxHide fxShow.gt-lg>
<h2 [ngClass]="{'gradient' : this.gradient,'lighter':lighter, 'zoom':zoom, 'scale':1.2}" style="margin: 0;" class="font">
hoverMe
</h2>
</div>
</div>
Typescript:
import {Component, Input, OnInit} from '#angular/core';
#Component({
selector: 'iet-box-academy',
templateUrl: './box-academy.component.html',
styleUrls: ['./box-academy.component.scss']
})
export class BoxAcademyComponent implements OnInit {
#Input() scale = 1;
#Input() img = '';
#Input() title = 'TITOLO';
#Input() descr = '';
#Input() align = "centerer";
#Input() lighter = false;
#Input() zoom = true;
#Input() gradient: boolean = false;
constructor() {
}
ngOnInit(): void {
}
}
CSS:
.container {
position: relative;
text-align: center;
color: black;
}
.zoom {
transition: transform .2s; /* Animation */
margin: 0 auto;
}
.zoom:hover {
transform: scale(1.5);
color: #00D3FF;
}
https://jsfiddle.net/wdfc7g9a/14/
You can add the :hover to the parent and add a child selector:
Change:
.zoom:hover {
transform: scale(1.5);
color: #00D3FF;
}
To:
.container:hover .zoom {
transform: scale(1.5);
color: #00D3FF;
}
Demo:
.container {
border: 1px solid red;
}
.container:hover .zoom {
background: yellow;
}
<div class="container">
This is a text
<div class="zoom">highlight this text</div>
More text
</div>
I recommend you to use centered-text class instead of zoom class. Because it is easier to give a transparent padding to it so you can have the "approaching" animation played without needing to hover directly on the text.
This code will fix your problem the moment you copy paste it to your Custom CSS:
.centered-text {
transition: all 0.3s;
margin: 0 auto;
padding:13px;
}
.centered-text:hover {
transform: scale(1.4);
color: #00D3FF;
transition: all 0.3s;
}

trying to do a gallery with lightbox but does not pop up

I'm new on angular,
And I'm trying to make a gallery with a lighbox that shows up when an image is clicked.
I already try the examples with bootstrap and also tried to do it by myself, but when I click on the img-link, the links throws me up to the home page maybe there is a config that I didn't know how to use it or is missing.
I don't know if its a problem about routes or if I need to do other commponent for that.
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
import { Component, OnInit } from '#angular/core';
import { CuadrosService, Cuadro } from '../cuadros.service';
#Component({
selector: 'app-galeria',
templateUrl: './galeria.component.html',
styleUrls: ['./galeria.component.css']
})
export class GaleriaComponent implements OnInit {
Cuadro:any [] = [];
constructor(private _cuadosService:CuadrosService ) {
console.log("constructor")
}
ngOnInit(): void {
this.Cuadro = this._cuadosService.getCuadros();
console.log(this.Cuadro);
}
}
.container-galeria {
display: flex;
flex-wrap: wrap;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 400px;
background-color: rgba(0, 0, 0, .80);
justify-content: space-around;
}
.img-galeria {
width: 100%;
height: 100%;
object-fit: cover;
padding: 10px;
}
.img-galeria:hover {
transform: scale(1.05);
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.3);
}
.item-galeria:nth-child(1) {
grid-column-start: span 2;
}
.item-galeria:nth-child(2) {
grid-row-start: span 2;
}
.lightbox {}
.lightbox:active {
display: block;
position: fixed;
flex-wrap: wrap;
height: fit-content;
width: fit-content;
max-width: 1200px;
max-height: 800px;
background-color: rgba(0, 0, 0, 0.3);
z-index: 2000;
justify-content: center;
}
<hr>
<div class="container-galeria container">
<div class=" lighbox item-galeria col-4" *ngFor="let cuadro of Cuadro">
<img class="img-galeria" [src]="cuadro.foto" alt="">
</div>
</div>
<app-footer></app-footer>
Any idea? or somthing wrong in my code?
Thanks for the help.
So the way I am seeing it your lightbox is missing a t in the class name.
Secondly I would set a onclick listener for each image to remove and set an active class instead of using the :active css selector. The css selector :active will be removed as soon as the person stops clicking.
You will have to Pass your Element in to the function to determine what image will be active.
Also you might want to add a function to exit the active mode
<div class="lightbox item-galeria col-4" *ngFor="let cuadro of Cuadro" (click)="toggleActive($event, item)">
<img class="img-galeria" [src]="cuadro.foto" alt="">
</div>
The function:
import { Component, OnInit } from '#angular/core';
import { CuadrosService, Cuadro } from '../cuadros.service';
#Component({
selector: 'app-galeria',
templateUrl: './galeria.component.html',
styleUrls: ['./galeria.component.css']
})
export class GaleriaComponent implements OnInit {
Cuadro:any [] = [];
constructor(private _cuadosService:CuadrosService ) {
console.log("constructor")
}
ngOnInit(): void {
this.Cuadro = this._cuadosService.getCuadros();
console.log(this.Cuadro);
}
toggle(event, item): void {
if(item.classList.contains("active")){
item.classList.remove("active"));
}else{
item.classList.add("active"));
}
}
I might add that I didn't test the code and some parts of code may need a little tweak.
I hope I could help.

Draw a border inside video with angular and CSS

I am writing a SPA which very simply shows video from webcam, draws a rectangle on it (so you can place a document where the edges align with the drawn box) then I press a button to capture that image.
Everything works except for drawing the box. I want it to be somewhat inside the div, so it's not around the video but actually on the video, aligning the sides but with a, let's say, 25px difference. I now just draw a border with CSS but I'm guessing there's better solutions I just can't seem to find them.
Edit: I tried with the drawImge() - function too, didn't seem to find a way to make it work. Haven't figured out why though.
app.component.ts:
import { Component, ElementRef, OnInit, Renderer2, ViewChild } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
#ViewChild('video', { static: true }) videoElement: ElementRef;
#ViewChild('canvas', { static: true }) canvas: ElementRef;
videoWidth = 0;
videoHeight = 0;
constraints = {
video: {
facingMode: "environment",
width: { ideal: 4096 },
height: { ideal: 2160 }
}
};
constructor(private renderer: Renderer2) {}
ngOnInit() {
this.startCamera();
}
startCamera() {
if (!!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)) {
navigator.mediaDevices.getUserMedia(this.constraints).then(this.attachVideo.bind(this))
.catch(this.handleError);
} else {
alert('Sorry, camera not available.');
}
}
attachVideo(stream) {
this.renderer.setProperty(this.videoElement.nativeElement, 'srcObject', stream);
this.renderer.listen(this.videoElement.nativeElement, 'play', (event) => {
this.videoHeight = this.videoElement.nativeElement.videoHeight;
this.videoWidth = this.videoElement.nativeElement.videoWidth;
});
}
capture() {
this.renderer.setProperty(this.canvas.nativeElement, 'width', this.videoWidth);
this.renderer.setProperty(this.canvas.nativeElement, 'height', this.videoHeight);
this.canvas.nativeElement.getContext('2d').drawImage(this.videoElement.nativeElement, 0, 0);
}
drawImge(videoHeight, videoWidth){
var video = document.querySelector("#webCamera");
var canvas = document.querySelector("#videoCanvas");
var ctx = this.canvas.nativeElement.getContext('2d');
ctx.rect(0,0,videoWidth,videoHeight);
ctx.lineWidth = "6";
ctx.strokeStyle = "red";
ctx.stroke();
}
handleError(error) {
console.log('Error: ', error);
}
}
app.component.html
<div class="container vh-100">
<div class="d-flex flex-column align-items-center">
<div class="p-1" >
<video #video class="vid" id="canvas1" autoplay></video>
</div>
<div class="pb-2">
<button class="btn btn-primary" (click)="capture()">Capture Image</button>
</div>
<div class="p-1">
<canvas #canvas class="vid"></canvas>
</div>
</div>
</div>
app.component.scss
#canvas1{
border-style: inset;
border-width: 300px;
border: solid 3px green;
}
Ok guys,
I found a really easy solution with CSS, it is however a bit messy as it's hardcoded. Also all the other drawings on screen should be animateable. Meaning i.e. an arrow drawn on screen moving slightly up, resetting and making the motion again untill someone uses the capture button.
This to only say I'm still looking for a solution that's not just CSS if anyone might have one.
As for the solution:
app.component.html:
<div class="container vh-100">
<div class="d-flex flex-column align-items-center">
<div class="p-1 wrapper" >
<video #video class="vid" id="canvas1" autoplay></video>
<div class="trapeziod"></div>
<div class="rect"></div>
</div>
<div class="pb-2">
<button class="btn btn-primary" (click)="capture()">Capture Image</button>
</div>
<div class="p-1">
<canvas #canvas class="vid"></canvas>
</div>
</div>
</div>
So just another div in the div where the video resides, called rect.
Then:
app.component.scss
#canvas1{
border-style: inset;
border-width: 300px;
border: solid 2px black;
}
.trapezoid {
width: 50px;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-bottom: 100px solid #7FFF00;
}
.wrapper{
position: relative;
}
.rect{
position: absolute;
left: calc(10% - 90px);
top: calc(20% - 100px);
border: solid 8px #7FFF00;
width: 1060px;
height: 580px;
}
This is perfectly how it should look.

Angular Animations: div enter behind another div

I'm trying to do a simple thing in my Angular 7 application.
Basically, I have a title and when I click on it, a comment will ease-in from above to place itself just below the title. The problem is, when the comment is moving, it shows on top of the title which is not quite the effect wanted.
I tried adding a z-index property on the CSS to elevate the title but to no avail.
app.component.ts
import { Component } from '#angular/core';
import { trigger, transition, style, animate } from '#angular/animations';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ],
animations: [
trigger('slideInOut', [
transition(':enter', [
style({transform: 'translateY(-100%)'}),
animate('200ms ease-in', style({transform: 'translateY(0%)'}))
]),
transition(':leave', [
animate('200ms ease-in', style({transform: 'translateY(-100%)'}))
])
])
]
})
export class AppComponent {
visible = false;
showMessage() {
this.visible = !this.visible;
}
}
app.component.html
<div (click)="showMessage()">
<div class="title">Click here to reveal the comment</div>
<div class="title">25/04/2019 17:30:00</div>
<div class="comment" *ngIf="visible" [#slideInOut]>Lorem ipsum...</div>
</div>
app.component.css
.title {
background-color: aqua;
z-index: 1000;
cursor: pointer;
}
.comment {
background-color: red;
z-index: 0;
}
I created a StackBlitz to show the problem.
Thanks for your help!
In order to z-index work. You need to add position: relative or absolute to the element. In your case, add also position: relative to the .title.
You can add the following:
.title, .comment{
position:relative;
}
z-index only works on positioned elements (position: absolute, position: relative, position: fixed, or position: sticky).
See Fork

Slide a div from the bottom underneath another div with angular animations

As you can see in the screenshot below, I hava a tab on the bottom of my page. When I click on it, I want it to slide underneath the <div> containing "Test" using angular animations. The problem is, that the pagesize should be responsive and therefore I cannot use px-values. I tried percentage as well, but that value refers to my tab-div, not the overall height.
Screenshot
My component:
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss'],
animations: [
trigger('tabState', [state('default', style({
transform: 'translateY(0)'
})
),
state('open', style({
transform: 'translateY(-100%)'
})),
transition('default <=> open', animate(500))
])
]})
export class TestComponent {
state = 'default';
onComeIn() {
this.state === 'default' ? this.state = 'open' : this.state = 'default';
}
}
My HTML:
<div class="mainContainer">
<mat-toolbar color="primary">
<div class="d-flex align-items-center justify-content-between">
<span>Test</span>
</div>
</mat-toolbar>
<div class="mainContentContainer">
<div class="d-flex flex-column" style="height: 100%">
<div>content</div>
<div class="mt-auto">
<div class="tabContainer" [#tabState]="state">
<div class="tab" (click)="onComeIn()">Tab</div>
</div>
</div>
</div>
And finally the css:
.tab {
box-sizing: border-box;
height: 4.2em;
width: 33%;
background-color: white;
padding: 1em 1.2em 0.45em 1.2em;
border-radius: 0.5em 0.5em 0 0;
box-shadow: 0 0.05em #b7b7b7;
}
.mainContainer {
width: 100%;
display: flex;
flex-direction: column;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.mainContentContainer {
flex: 1;
background-color: #455864;
}
The issue is more about css :
I changed the initial value of the tabContainer class to this :
.tabContainer {
position: fixed;
bottom: 0;
width: 100%;
}
Then in the animation definition, removed the bottom and added the top one :
state('open', style({
bottom: 'initial',
top: '20px'
})),
Here is the running example in editor.