Perché <feDropShadow> non viene visualizzato?

Sep 30 2020

Modifica : cercando di creare un mcve non sono riuscito a riprodurre il problema. Ora sono completamente sconcertato. Funziona su codesandbox, non nel mio progetto.


Domanda iniziale
Desidero creare un elemento SVG inline dinamico e mappare la sua rotazione su un file [(ngModel)]. Nulla di bello.

La parte di fantasia è che voglio usare a <filter>con a <feDropShadow>. E voglio che l'ombra sia dinamica (sempre rivolta verso l'alto, indipendentemente dalla rotazione dell'ago). È qualcosa che ho fatto prima di usare Vue.

Ecco un violino che dimostra l'effetto: https://jsfiddle.net/websiter/y4ghan0k/

Ma, per quanto mi riguarda, non riesco a farlo <feDropShadow>funzionare in Angular quando <svg>è inline nel modello. Semplicemente non verrà visualizzato. Nessun errore o avviso. Se lo inserisco come <img src="path/to/svg">funziona come previsto (viene visualizzata l'ombra), ma poi non riesco più a ruotare il percorso, poiché l'elemento trasformato deve essere figlio dell'elemento che porta il filtro.

Nota che non è a causa di questo problema con il filtro url () : sto anteponendo al filtro this.location.path().

Ecco il succo del mio codice angolare:

component.ts:

import { Location } from '@angular/common';

export class SomeComponent {
  constructor(private location: Location) {}
  dsLink = `url(${this.location.path()}#drop-shadow)`;
}

component.html:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="180" y="100"
     viewBox="0 0 180 100" xml:space="preserve">
  <defs>
    <filter xmlns="http://www.w3.org/2000/svg" id="drop-shadow" height="130%">
      <feDropShadow dx="0" dy="-4" flood-color="rgba(0,0,0,.65)"/>
    </filter>
  </defs>
  <g [attr.filter]="dsLink">
    <path fill="#fff" d="M102.2,89.5c0-0.1,0-0.1,0-0.2c0-0.2,0-0.4-0.1-0.6L92.9,6.8c-0.1-0.8-3.2-0.9-3.3,0
      L78.7,88.5c-0.1,0.2-0.1,0.4-0.1,0.6c0,0.1,0,0.1,0,0.2l0,0.1c0,0,0,0.1,0.1,0.1c0.5,2.4,5.6,4.4,11.7,4.4
      c6.2,0.1,11.2-1.8,11.8-4.2c0,0,0.1-0.1,0.1-0.1L102.2,89.5z">
    </path>
  </g>
</svg>

Per semplicità, ho rimosso il [(ngModel)]percorso da che dovrebbe ruotare l'ago.

Il filtro url()sembra essere corretto, non ci sono errori. Ma l'ombra non viene visualizzata.

C'è qualcosa di speciale che devo fare / sapere per rendere in <svg>linea gli elementi della maniglia angolare ?

Cosa mi manca?

Risposte

1 tao Sep 30 2020 at 05:27

Alla fine l'ho risolto, quindi lo sto postando qui, sperando che possa aiutare gli altri.

In breve: utilizza ID univoci per i filtri in ciascuna delle istanze dei tuoi componenti. Altrimenti, ogni istanza utilizzerà il primo filtro trovato in DOM (con quell'id) e se quel filtro si trova all'interno di un genitore con display: none, visibility:hiddeno opacity: 0, l'applicazione del filtro renderà invisibile anche tutto ciò a cui lo applichi.


Il problema aveva a che fare con il fatto che stavo usando lo stesso componente in schede diverse. Questo ha creato istanze separate del componente, ognuna delle quali utilizza lo stesso id ( #drop-shadow). Sebbene avere ID duplicati sia ovviamente HTML non valido, questo non sarebbe stato un problema se non avessimo a che fare con i filtri. Perché, dal momento che <defs>sono identici, non sarebbe molto importante se il componente nella quarta scheda utilizzasse il <defs>definito dal componente nella prima scheda.

Tranne quando si tratta di <filter>s, perché stanno effettivamente calcolando, pixel per pixel, il risultato del rendering, in modo dinamico. Ciò significa che, quando la <svg>definizione di <filter>non è renderizzata, l'utilizzo del filtro farà calcolare al browser (pixel per pixel) il risultato applicando il filtro e risulterà sempre che tutti i pixel saranno invisibili.

Quindi la soluzione è assegnare un ID univoco in ogni istanza separata del componente.