将 CodePen babel 和 stylus 转换为 Angular 5 和 scss

Converting CodePen babel and stylus to Angular 5 and scss

我找到了这个简洁的 CodePen 动画按钮组:

https://codepen.io/Zaku/pen/reGRBg

我正在尝试将其实施到我的 angular 5 项目中。

我做的还不错。我可以看到 svg 发生了一些事情,但它没有过渡,它是一个黑色阴影:

以下是我所做的更改:

mycomponent.ts :

import {Component, ElementRef, OnInit} from '@angular/core';

@Component({
  selector: 'app-mycomponent',
  templateUrl: './mycomponent.html',
  styleUrls: ['./mycomponent.scss']
})
export class myComponent implements OnInit {

  pathLength = 68 * 2 + 200;
  group;
  buttons;
  svg;
  path;
  currentPath;
  currentIndex;

  constructor(private elRef:ElementRef) {}

  ngOnInit() {
    this.group = this.elRef.nativeElement.querySelector('.btn-group');
    this.buttons = Array.prototype.slice.call(
      this.group.querySelectorAll(".btn")
    );
    console.log(this.buttons);
    this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    this.svg.setAttribute(
      "viewbox",
      `-1 -1 ${160 * this.buttons.length + 2} 42`
    );
    this.path = document.createElementNS("http://www.w3.org/2000/svg", "path");
    this.currentPath = "M -80, 40";
    this.currentIndex = -1;
    this.activateIndex(
      this.buttons.indexOf(this.group.querySelector(".active"))
    );
    this.group.appendChild(this.svg);
    this.svg.appendChild(this.path);
    this.refreshPath();
  }

  onClick(e) {
    const index = this.buttons.indexOf(e.srcElement || e.target);
    this.activateIndex(index);
  }

  refreshPath() {
    this.path.setAttributeNS(null, "d", this.currentPath);
    this.path.style.strokeDashoffset =
      (-this.path.getTotalLength() + this.pathLength) * 0.9965;
  }

  center(index) {
    return index * 160 + 80;
  }

  removeClass(str) {
    if (this.buttons[this.currentIndex]) {
      this.buttons[this.currentIndex].classList.remove(str);
    }
  }

  addClass(str) {
    if (this.buttons[this.currentIndex]) {
      this.buttons[this.currentIndex].classList.add(str);
    }
  }

  activateIndex(index) {
    const lastCenter = this.center(this.currentIndex);
    const nextCenter = this.center(index);
    const offset = index < this.currentIndex ? -50 : 50;
    const curve = index < this.currentIndex ? -30 : 30;
    this.currentPath += `
    L ${lastCenter + offset}, 40
    C ${lastCenter + offset + curve}, 40
      ${lastCenter + offset + curve},  0
      ${lastCenter + offset},  0
    L ${lastCenter - offset},  0
    C ${lastCenter - offset - curve},  0
      ${lastCenter - offset - curve}, 40
      ${lastCenter - offset}, 40
    L ${nextCenter + offset}, 40
    C ${nextCenter + offset + curve}, 40
      ${nextCenter + offset + curve},  0
      ${nextCenter + offset},  0
    L ${nextCenter - offset},  0
    C ${nextCenter - offset - curve},  0
      ${nextCenter - offset - curve}, 40
      ${nextCenter - offset}, 40
    L ${nextCenter}, 40`;
    this.removeClass("active");
    this.currentIndex = index;
    this.addClass("active");
    this.refreshPath();
  }
}

mycomponent.html:(您会注意到我添加了 angular 点击观察者来代替处理操作)

<div class="container">
  <div class="btn-group">
    <div (click)="onClick($event)" class="btn">First</div>
    <div (click)="onClick($event)" class="btn active">Middle</div>
    <div (click)="onClick($event)" class="btn">Last</div>
  </div>
</div>

mycomponent.scss:

$easing: cubic-bezier(0, 0.8, 0.2, 1);
$duration: 1s;

  .container {
    color: #19cc95;
    text-align: center;
  }

  .btn-group {
    position: relative;
    display: inline-block;
    .btn {
      cursor: pointer;
      float: left;
      height: 40px;
      width: 160px;
      line-height: 40px;
      text-align: center;
      -webkit-user-select: none;
      transition: font-size 0.3s ease;
      &:active {
        font-size: 0.8em;
      }
    }
    svg {
      z-index: -1;
      top: 0;
      left: 0;
      position: absolute;
      width: 100%;
      height: 100%;
      overflow: visible;
    }
    path {
      fill: none;
      stroke: #19cc95;
      stroke-dasharray: 334.9, 99999;
      transition: stroke-dashoffset 1s cubic-bezier(0, 0.8, 0.2, 1);
      stroke-width: 1;
    }
  }

以上还不错。 'active' class 被正确设置为点击。并且 svg 黑色阴影发生变化。

显然 svg 应该覆盖在菜单上,但并没有发生。

我错过了什么?

由于 svg 元素是动态创建的,您必须在动态创建 svg 的组件上使用 encapsulation: ViewEncapsulation.None 以确保来自 的样式.component.scss 应用于一切。

我复制了你的代码并运行它成功了,但稍作修改。

component.html

<div class="container">
  <div #btnGroup class="btn-group">
    <div class="btn" (click)="onClick($event)">First</div>
    <div class="btn active" (click)="onClick($event)">Middle</div>
    <div class="btn" (click)="onClick($event)">Last</div>
  </div>
</div>

component.ts

import {Component, ElementRef, OnInit, ViewChild, ViewEncapsulation} from '@angular/core';

@Component({
  selector: 'app-svg-buttons',
  templateUrl: './svg-buttons.component.html',
  styleUrls: ['./svg-buttons.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class SvgButtonsComponent implements OnInit, AfterViewInit {
  @ViewChild('btnGroup') btnGroup: ElementRef;

  pathLength = 68 * 2 + 200;
  group;
  buttons;
  svg;
  path;
  currentPath;
  currentIndex;

  constructor(private elementRef: ElementRef) {
  }

  ngOnInit() {
  }

  ngAfterViewInit() {
    this.initializeGroup(this.btnGroup.nativeElement);
  }

  initializeGroup(group) {
    this.group = group;

    this.buttons = Array.prototype.slice.call(
      this.group.querySelectorAll('.btn')
    );

    this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    this.svg.setAttribute('viewbox',
      `-1 -1 ${160 * this.buttons.length + 2} 42`
    );
    this.path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    this.currentPath = 'M -80, 40';
    this.currentIndex = -1;
    this.activateIndex(
      this.buttons.indexOf(
        this.group.querySelector('.active')
      )
    );
    this.group.appendChild(this.svg);
    this.svg.appendChild(this.path);
    this.refreshPath();
  }

  onClick(e) {
    const index = this.buttons.indexOf(e.srcElement || e.target);
    this.activateIndex(index);
  }

  refreshPath() {
    this.path.setAttributeNS(null, 'd', this.currentPath);
    this.path.style.strokeDashoffset = (-this.path.getTotalLength() + this.pathLength) * 0.9965;
  }

  center(index) {
    return index * 160 + 80;
  }

  removeClass(str) {
    if (this.buttons[this.currentIndex]) {
      this.buttons[this.currentIndex].classList.remove(str);
    }
  }

  addClass(str) {
    if (this.buttons[this.currentIndex]) {
      this.buttons[this.currentIndex].classList.add(str);
    }
  }

  activateIndex(index) {
    const lastCenter = this.center(this.currentIndex);
    const nextCenter = this.center(index);
    const offset = index < this.currentIndex ? -50 : 50;
    const curve = index < this.currentIndex ? -30 : 30;
    this.currentPath += `
      L ${lastCenter + offset        }, 40
      C ${lastCenter + offset + curve}, 40
        ${lastCenter + offset + curve},  0
        ${lastCenter + offset        },  0
      L ${lastCenter - offset        },  0
      C ${lastCenter - offset - curve},  0
        ${lastCenter - offset - curve}, 40
        ${lastCenter - offset        }, 40
      L ${nextCenter + offset        }, 40
      C ${nextCenter + offset + curve}, 40
        ${nextCenter + offset + curve},  0
        ${nextCenter + offset        },  0
      L ${nextCenter - offset        },  0
      C ${nextCenter - offset - curve},  0
        ${nextCenter - offset - curve}, 40
        ${nextCenter - offset        }, 40
      L ${nextCenter                 }, 40
    `;
    this.removeClass('active');
    this.currentIndex = index;
    this.addClass('active');
    this.refreshPath();
  }
};