Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 4: show overlay on mouseover with animation based on mouse direction

Tags:

angular

i would like to get this in an Angular4 Component without using Jquery.

I need to show an overlay div with animation based on the mouse direction, both mouseover and mouseout. I tried to rewrite the example code in my component but i cannot get the width and height of the event.target.

My div:

<div (mouseover)='showOverlay($event)'>div to hover</div>

Component:

export class MyComponent implements OnInit {

    constructor(private _el: ElementRef) {
    }

    ngOnInit() {

    }

    showOverlay(e) {
        const target = e.target;
        console.log(target._el.nativeElement); // Returns undefined
        console.log(target.nativeElement); // Returns undefined
        console.log(target.nativeElement.width); // Error
        console.log(target._el.nativeElement.width); // Error
    }
}

Any idea?

Thank you

like image 569
SBO Avatar asked Aug 28 '17 08:08

SBO


2 Answers

I like this effect, so I made a component with angular animations: https://stackblitz.com/edit/angular-fftwpg.

import { Component, HostListener } from '@angular/core';
import { AnimationEvent } from '@angular/animations';
import {
  trigger,
  state,
  style,
  animate,
  transition
} from '@angular/animations';

const animateIn = '0.15s ease-in';
const animateOut = '0.25s ease-out';

const styleIdle = { transform: 'translate3d(0, 0, 0)' };
const styleTop = { transform: 'translate3d(0, -100%, 0)' };
const styleRight = { transform: 'translate3d(100%, 0, 0)' };
const styleBottom = { transform: 'translate3d(0, 100%, 0)' };
const styleLeft = { transform: 'translate3d(-100%, 0, 0)' };

export const HoverContainerAnimations = [
  trigger('hover', [
    state('*', style(styleIdle)),
    transition('* => in-left', [
      style(styleLeft), animate(animateIn)
    ]),
    transition('* => in-right', [
      style(styleRight), animate(animateIn)
    ]),
    transition('* => in-top', [
      style(styleTop), animate(animateIn)
    ]),
    transition('* => in-bottom', [
      style(styleBottom), animate(animateIn)
    ]),
    transition('* => out-right', [
      animate(animateOut, style(styleRight))
    ]),
    transition('* => out-left', [
      animate(animateOut, style(styleLeft))
    ]),
    transition('* => out-top', [
      animate(animateOut, style(styleTop))
    ]),
    transition('* => out-bottom', [
      animate(animateOut, style(styleBottom))
    ]),
  ])
];


@Component({
  // tslint:disable-next-line:component-selector
  selector: 'hover-container',
  template: `
    <ng-content></ng-content>
    <div class="overlay" 
        *ngIf="state" 
        [@hover]="state" 
        (@hover.done)="onDone($event)">
      <ng-content select="[overlay]"></ng-content>
    </div>`,
  styleUrls: ['./hover-container.component.css'],
  animations: HoverContainerAnimations,
})
export class HoverContainerComponent {
  state;

  @HostListener('mouseenter', ['$event'])
  @HostListener('mouseleave', ['$event'])
  onHover(event: MouseEvent) {
    const direction = event.type === 'mouseenter' ? 'in' : 'out';
    const host = event.target as HTMLElement;
    const w = host.offsetWidth;
    const h = host.offsetHeight;

    const x = (event.pageX - host.offsetLeft - (w / 2)) * (w > h ? (h / w) : 1);
    const y = (event.pageY - host.offsetTop - (h / 2)) * (h > w ? (w / h) : 1);
    const states = ['top', 'right', 'bottom', 'left'];
    const side = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180) / 90) + 3) % 4;
    this.state = `${direction}-${states[side]}`;
  }

  onDone(event: AnimationEvent) {
    this.state = event.toState.startsWith('out-') ? null : this.state;
  }

}
like image 112
Sasxa Avatar answered Oct 24 '22 10:10

Sasxa


I think it would be better to write a Directive so you can 'reuse' it for each <div>.

Directive:

@Directive({
    selector: '[over]'
})
export class OverDirective {
    constructor(private elementRef: ElementRef) {
    }

    @HostListener('mouseover', ['$event'])
    onOver(event: MouseEvent): void {
        console.log(this.elementRef.nativeElement.offsetHeight)
    }
}

HTML:

<div over id="myDiv">div to hover</div>

If you still want to access to the element from inside the component via template variable:

HTML

<div id="div2" (mouseover)="onMouseOver($event, toto)" #toto>an other div to hover</div>

Typescript

onMouseOver(event, elem)
  {
    console.log(elem.offsetHeight);
 }

Plunker

like image 31
Vega Avatar answered Oct 24 '22 11:10

Vega