Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to create filter that is only used on certain elements in the SVG? #63

Open
sqwarellc opened this issue Mar 18, 2023 · 24 comments
Open

Comments

@sqwarellc
Copy link
Contributor

For my project, I am developing a control surface that is a window-filling SVG, with various graphical elements to comprise buttons, and I would like to use filters to indicate the button states as well as for simple decoration. In particular, feDropShadow will be used a lot, but not on the whole image.

I have followed the example on creating a reusable filter, such as:
var filter = new SVG.Filter();

However, when I attach this filter to my main image as well as an element, it affects the whole image...

myImage.filterWith(filter);
myParticularShape.filterWith(filter);

That seems to make sense. But if I only attach it to the elements that I want to affect, it doesn't work, because the filter information doesn't show up in the definitions section of the image. (The individual elements do include references to the filter, but there is no definition.)

Is there a way to define the filter in the SVG without using it except on particular elements?

(And to kill two birds with one stone, if your response includes an example of invoking feDropShadow on a cricle, while still preserving the appearance of the circle itself, that would really help me out.)

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 18, 2023

If you are creating a reusable filter, you have to make sure, that it is attached to the defs. To be honest i am quite suprised why it works when you filter the whole image. It should either work all the time or never. Maybe you discovered a bug. However, in order to attach the filter to the defs, just use myImage.defs().add(filter).
Now your "filterWith" calls should succeed.

If you want one filtered element and one non-filtered element its actually easiest to duplicate your element and put the non filtered element on top of the filtered. I am not quite sure if there is a way to only do it with one element.

What you CAN do is, create the element as symbol and reuse it with the use-element.
You can read more about that on mdn: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use

@sqwarellc
Copy link
Contributor Author

sqwarellc commented Mar 18, 2023

Thank you, I was erroneously using filterWith. When I saw the image.filterWith() line in the example code, I assumed it was meant for the SVG image, not just some image element. Sorry for the confusion. I changed to myImage.defs().add(filter) and it is now working correctly.

A new wrinkle, when I use dropShadow, my IDE suggests that the parameters are (x, y, stdDeviation) but the output in the filter definition is different.

For example, if I use
myFilter.dropShadow(8, 12, 2);
the output is:
<feDropShadow id="SvgjsFeDropShadow1000" result="SvgjsFeDropShadow1000" in="8" dy="2" dx="12">

My value for "x" goes to "in".

Or should I be calling dropShadow with a different style of arguments?

Sorry if this is my coding ignorance, still learning.

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 18, 2023

The IDE should give you the correct typings if myFilter is a <filter> object. The typings with missing "in" is for chaining of filters which is not applicable here. Is your myFilter really the <filter> or accidentially the <feDropShadow>?

// EDIT: tbh, i had to look into the code to know whats going on. I didnt use filters a lot myself

@sqwarellc
Copy link
Contributor Author

From my source:

// creating the SVG
stopJambDrawing = SVG().addTo('body');

// later, setting up a reusable filter
var myFilter = new SVG.Filter();
myFilter.dropShadow(8, 12, 2);
stopJambDrawing.defs().add(myFilter);

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 18, 2023

And your IDE gives you the wrong types?

This is from the types file:

dropShadow (in1: EffectOrString, dx: number, dy: number, stdDeviation: number): DropShadowEffect

As you can see, instance methods of filter have the in property as first parameter. So I have no idea whats going on on your side. As a matter of fact, this method takes 4 parameters and the first one is the in-parameter which should be "SourceGraphic" in your example (https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/in)

@sqwarellc
Copy link
Contributor Author

Yes, my IDE isn't showing the in-parameter. Screen shot attached. I'm using phpStorm on MacOS.

Screen Shot 2023-03-18 at 12 46 04 PM

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 18, 2023

weird. you are writing javascript right now. Maybe phpstorm mixes it up? You could try to use typescript instead. I cannot find a mistake in the code tho.

// EDIT: even if it complains, just add 4 parameters to make it work and silence the error

@sqwarellc
Copy link
Contributor Author

Thanks, it accepted four parameters without complaining and my drop shadow now works.

Not sure why phpStorm is behaving that way, phpStorm does support JavaScript and the editor believes that the code block I am working on is JavaScript. At least I can move forward, I just won't be able to rely on the suggestions from my IDE to learn how to use this library, I'll have to go look in the source at various times.

I appreciate your multiple responses to my questions. If you'd ever like to collaborate in updating the examples with tutorials aimed at less experienced coders, I'd be happy to do the writing if you can step me through some basics sometime.

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 18, 2023

I think the problem is, that phpstorm can only do type-guessing here because you are not using typescript but javascript. So it tries to help as best as it can but sometimes gets it wrong? Not sure how good phpstorm is. Most people use VSCode for javascript and typescript nowadays.

I would be very happy to get some more examples into the docs. I can review the code and see if it has some obvious flaws and we can discuss if you have any questions :).

(You already know how to create a PR now so thats good :D)

@sqwarellc
Copy link
Contributor Author

Thank you - should I create a new issue for discussing suggestions for the docs, or should we take this to private email/messaging?

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 18, 2023

I would put it into the PR so people can follow along. Create a new branch, make teh changes in the readme and create a PR. If you then have questions to teh code, we can discuss it there :)

@sqwarellc
Copy link
Contributor Author

Fair enough, but before I can suggest changes to the docs I really need to wrap my head around how all this works more.

For example, once I have created my drop shadow, how do I affect its overall opacity? When I try to add flood opacity by changing the flood effect, my shadow turns into a square rather than representing the original shape:

myFilter = new SVG.Filter();
myFilter.dropShadow('SourceGraphic', 12, 12, 3);
myFilter.flood('#000', .25);
stopJambDrawing.defs().add(myFilter);

Now, if I were to create the filter by hand-coding it according to this example, it does work:

https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feDropShadow

But I can't figure out how to set it up properly using the library.

Examining the output of my code, I actually get the flood/opacity parameter on a separate line rather than in the setup for the drop shadow. (I get the same if I try to chain it.)

<feDropShadow id="SvgjsFeDropShadow1000" result="SvgjsFeDropShadow1000" in="SourceGraphic" stdDeviation="3" dy="12" dx="12"></feDropShadow>

<feFlood id="SvgjsFeFlood1001" result="SvgjsFeFlood1001" in="SourceGraphic" flood-opacity="0.25" flood-color="#000000"></feFlood>

So, what I'm looking for as an eager-to-learn beginner is for the docs and examples to walk me through an introduction of how to add various common filter parameters and in what order, best way to code, etc. I cant suggest what to write until I know how to do it. :-)

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 18, 2023

Well, I cant serve with that much of support because i am working on this in my spare time.
However, it seems like you now want to chain filters. And that would something like that:

myFilter = new SVG.Filter();
myFilter.dropShadow('SourceGraphic', 12, 12, 3).flood('#000', .25);

@sqwarellc
Copy link
Contributor Author

Hi - I did try chaining it just like that earlier, and got the same undesired result. (I did just try copying/pasting your suggestion, just in case I got it wrong originally, same problem.)

Here are two partial screen shots of the output, the first is without the flood, and the second is with flood added via chaining.

Screen Shot 2023-03-18 at 2 32 41 PM
Screen Shot 2023-03-18 at 2 32 59 PM

(Don't worry about the shadow being cut-off in the first example, I already know how to fix that with size/move.)

If I write the filter by hand in a test SVG file, it looks like this, and works:

<feDropShadow in="SourceGraphic" dx="12" dy="12" stdDeviation="3" flood-color="#000" flood-opacity="0.5" />

But the output from the library, even when chaining, is in two entries:

<feDropShadow id="SvgjsFeDropShadow1000" result="SvgjsFeDropShadow1000" in="SourceGraphic" stdDeviation="3" dy="12" dx="12"></feDropShadow>
<feFlood id="SvgjsFeFlood1001" result="SvgjsFeFlood1001" in="SourceGraphic" flood-opacity="0.25" flood-color="#000000"></feFlood>

So, that's where I'm stuck.

Thanks again for your help. I do appreciate that it's a Catch-22: You're working on this in your spare time. I'd like to help improve the documentation for beginners, so you don't have to answer questions of a similar nature in the future, but I can't do that without help learning how to use the library properly. (I am able to follow the documentation for the main svg.js library, it's reasonably thorough. It's the filters where I am getting hung up.)

@sqwarellc
Copy link
Contributor Author

OK, I think I've found a solution on my own. Just let me know if this is a generally-acceptable usage. I can pass the values via attr like this and it works:

myShadow = myFilter.dropShadow('SourceGraphic', 12, 12, 3).attr({'flood-opacity': .5, 'flood-color': '#000'});

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 18, 2023

Ah i see, the problem is, that you want to use extra attributes on the drop shadow effect but the chaining actually creates a new flood effect. I wasnt aware, that feDropShadow can also take flood parameters.
However, in that case, it is simply one effect with more attributes and not 2 effects chained together.

The problem in the case of your example is, that flood has the wrong input. The Input should be "SvgjsFeDropShadow1000" but instead it is "SourceGraphic" again.

Your solution works ofc. But it doesnt show filter chaining

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 18, 2023

I also just looked at the code and saw that the flood effect doesnt have any inputs.
This can also be seen in the mdn docs: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feFlood
Thats what the chaining didnt work.

@sqwarellc
Copy link
Contributor Author

Thanks, I see now that "flood" is its own separate effect. I got confused because it has the same name as the parameters that can be supplied to other effects.

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 18, 2023

yeah I wasnt aware that flood can be an attribute. Its clearly missing in the api of this package as well (maybe its new-ish?)

@sqwarellc
Copy link
Contributor Author

sqwarellc commented Mar 18, 2023

It looks like the filter feDropShadow was added to the SVG spec in Version 1.2, but the flood-color and flood-opacity parameters were added to it (along with a bunch of others) in Version 2 in 2018:

https://www.w3.org/TR/filter-effects/#feDropShadowElement

Edit:

This document is a summary of which presentation attributes are used by which elements:
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation

@sqwarellc
Copy link
Contributor Author

I've been looking over the source and I think I can take a crack at adding the two new attributes to dropShadow. I'm going to attempt to make these edits and then generate a pull request.

@sqwarellc
Copy link
Contributor Author

Pull request submitted. Please let me know if I did things correctly, thanks.

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 19, 2023

I answered in the pr :)

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 20, 2023

Ah I see, we didnt implement much of the SVG2 spec since browsers couldnt really decide on anything lol

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants