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

Overlay image for object or some way to prevent zoom on object pattern/background #3621

Closed
dvideby0 opened this issue Jan 12, 2017 · 20 comments

Comments

@dvideby0
Copy link

dvideby0 commented Jan 12, 2017

Currently I am working on a project that requires us to use an object on the canvas to represent an "editable canvas". This editable canvas is where you can add, move, remove other shapes. There is some padding that we simulate around the editable canvas and the actual canvas (40px). When zooming we use relative pan to move around and simulate what overflow would be like if the actual canvas was the size of the zoomed canvas. Our issue is that we need to have guides or a "grid" on our editable canvas that remain the same size regardless of zoom. Essentially we would like for it to work just like the canvas overlay where zoom does not affect the overlay's scale while still allowing the editable canvas to scale. By setting repeat, the overlay would simply continue to repeat to fill the "zoomed" size. We have tried using pattern fills to accomplish this and just re-calculating the pattern size with each change in zoom but unfortunately it appears that once you use an svg for a pattern fill it is rasterized and is now distorted by the zoom.

The logic we were trying looks like below where floorPlan.item(0) is our editable canvas

let self = this;
    fabric.loadSVGFromString(self.dotsSVG, function(objects) {
      let group = new fabric.PathGroup(objects, {
        left: 0,
        top: 0,
        width: 10,
        height: 10
      });
      group.scaleX = 1 / zoomLevel;
      group.scaleY = 1 / zoomLevel;
      self.patternSourceCanvas = new fabric.StaticCanvas();
      self.patternSourceCanvas.add(group);
      self.patternSourceCanvas.renderAll();
      self.patternSourceCanvas.setDimensions({
        width: group.getWidth(),
        height: group.getHeight()
      });
      self.patternSourceCanvas = self.patternSourceCanvas.getElement();
      let pattern = new fabric.Pattern({
        source: self.patternSourceCanvas,
        repeat: 'repeat'
      });
      self.floorPlan.item(0).fill = pattern;
      self.floorPlan.renderAll();
    });

Here is a screen of what we have

screen shot 2017-01-12 at 3 08 28 pm

the lighter shaded area is our rectangle (editable canvas). When I zoom on the canvas the dots zoom as well. I would like to be able to keep the dots the same size while increasing the surface area of the editable canvas and simply have a repeating pattern continue to fill the zoomed size with dots. Something similar to your flag overlayVpt I believe

@dvideby0 dvideby0 changed the title Feature - Overlay image for object Overlay image for object or some way to prevent zoom on object pattern/background Jan 12, 2017
@asturur
Copy link
Member

asturur commented Jan 12, 2017

So you should define as overlay not an image but a fabric.Rect(). fill this fabric.Rect() with this pattern possibly made of one single square with the dotted corner. And then change the rect width/height instead of zooming it.

@dvideby0
Copy link
Author

@asturur We have 1000s of other objects that sit above the "editable canvas" that are bound to it. We would have to write a bunch of logic to handle re-scaling all of them as well as repositioning them to simulate what zoom already does.

@asturur
Copy link
Member

asturur commented Jan 12, 2017

Where are those dots? over or under the zoomable objects?

@dvideby0
Copy link
Author

We render them on a rect which is zoomable but then put all other objects on top of the rect. Like below

screen shot 2017-01-12 at 3 40 22 pm

@dvideby0
Copy link
Author

ideally it would look like below after zoomed. You can see that the editable canvas is now larger but the dots remain the same size.

screen shot 2017-01-12 at 3 45 58 pm

@dvideby0
Copy link
Author

dvideby0 commented Jan 12, 2017

Currently the only option I have is to create a 2nd canvas (2) that I put at a lower z-index with a rect that uses a repeating pattern for those dots. Then tie in all the panning and zooming events that canvas 1 has. When you zoom canvas 1 I have to resize and re position the rect on canvas 2 to match the size and position of the zoomed rect on canvas 1. Unfortunately this gets a bit buggy and I haven't been able to figure out how to accurately represent the absolute size of the rect on canvas 1 after being zoomed on a rect in canvas 2.

@asturur
Copy link
Member

asturur commented Jan 12, 2017

Better you check this code is easier and it will be a future feature:
https://github.com/kangax/fabric.js/pull/3554/files#diff-840d0c62989de96492c73f1de1f574d0R1461

start from renderFill and check the new introduced function that is there.
You will see there is an option called .scaleWithObject if you copy that portion of code ( give yourself some time, do not give up if the first copy paste does not work, i had it working ) and then set the pattern with .scaleWithObject = false, it will not scale anymore.

@dvideby0
Copy link
Author

dvideby0 commented Jan 12, 2017

Thank you very much for your reply! Will the repeat still continue to fill the space of the object? For our needs the dots still cover the entire "editable canvas". Please see this GIF to show expected behavior.

@asturur
Copy link
Member

asturur commented Jan 12, 2017

yes or would not be a pattern.

@asturur asturur closed this as completed Jan 12, 2017
@dvideby0
Copy link
Author

@asturur I have cloned the repo and then checked out HEAD to 700ee14 (send up to group the change on dirty flag (#3564)) and merged your pull in since that was the last commit your pull was compatible with. I ran the build and was able to verify that the flag for scaleWithObject is there. Unfortunately it is still scaling with the object. Am I missing something? This is the code I am using

let self = this;
    fabric.Image.fromURL('../../../assets/images/dots.png', function(img) {
      self.patternSourceCanvas = new fabric.StaticCanvas();
      self.patternSourceCanvas.add(img);
      self.patternSourceCanvas.renderAll();
      self.patternSourceCanvas.setDimensions({
        width: img.getWidth(),
        height: img.getHeight()
      });
      self.patternSourceCanvas = self.patternSourceCanvas.getElement();
      let pattern = new fabric.Pattern({
        source: self.patternSourceCanvas,
        repeat: 'repeat'
      });
      pattern.scaleWithObject = false;
      self.floorPlan.item(0).setFill(pattern);
      self.floorPlan.renderAll();
    });

@dvideby0
Copy link
Author

I have been able to simplify this code based off of another demo to

fabric.util.loadImage('../../../assets/images/dots.png', function(img) {
      let pattern = new fabric.Pattern({
        source: img,
        scaleWithObject: false
      });
      self.floorPlan.item(0).setFill(pattern);
      self.floorPlan.renderAll();
    });

@asturur
Copy link
Member

asturur commented Jan 13, 2017

So i got confuse between zoom and scale. It's fine the code is still valid.

the second way you posted to create the pattern is simpler and is what i would like people to use.

  1. i take care of loading my image in any way.
  2. i build the pattern that is immediately ready
  3. i assign to object

With the code you merged ( that will available in fabric later with a slightly different form but same functionalities ) you have new properties for the pattern:

angle
scaleWithObject
scaleX
scaleY

What you have to do for page zoom is to get zoom value ( canvas.getZoom() ) and set the pattern scaleX and scaleY to 1/zoom you rect that acts as board does not get properly scaled, just zoomed by canvas. This should do the trick.'

Consider also to set that rect instead of item(0) to backgroundImage for the canvas. probably it will save you some headache about not having around during selection.

@dvideby0
Copy link
Author

Is this the expected code (excluding the comment about item(0).

let self = this;
    fabric.util.loadImage('../../../assets/images/dots.png', function(img) {
      let pattern = new fabric.Pattern({
        source: img,
        repeat: 'repeat',
        scaleWithObject: false
      });
      pattern.scaleX = 1 / zoomLevel;
      pattern.scaleY = 1 / zoomLevel;
      self.floorPlan.item(0).setFill(pattern);
      self.floorPlan.renderAll();
    });

@asturur
Copy link
Member

asturur commented Jan 13, 2017

Yes but you have to update pattern.scaleX and pattern.scaleY every time you zoom.
Either with a reference to pattern or with item(0).fill.scaleX, item(0).fill.scaleY

@dvideby0
Copy link
Author

Ah ok. I was able to get the code to semi work by doing something like
DEMO: GIF

    let self = this;
    self.floorPlan.item(0).fill.scaleX = 1 / zoomLevel;
    self.floorPlan.item(0).fill.scaleY = 1 / zoomLevel;
    self.floorPlan.renderAll();

I found that it works better if I reset the pattern
DEMO GIF

let self = this;
    let pattern = self.floorPlan.item(0).fill;
    pattern.scaleX = 1 / zoomLevel;
    pattern.scaleY = 1 / zoomLevel;
    self.floorPlan.item(0).setFill(pattern);
    self.floorPlan.renderAll();

However, with each zoom it takes longer and longer to render. Once I start getting to a zoom of about 10 to 13 the whole thing just disappears.

@asturur
Copy link
Member

asturur commented Jan 14, 2017

So there are things to keep in mind.
You are trying yo get dots that pan and that do not zoom.
I do not know this rect if is on object stack, if is somewhere else.
I would really like to see a fiddle. I'm sure there are different solutions and simpler.

Since that object that keep the pattern is relatively simple, disable cache for it. if it is disappearing is because probably the fabric cache system is creating an enormous canvas for it.

@dvideby0
Copy link
Author

@asturur Turning off object caching did the trick. Unfortunately this build of fabric seems to have an issue with the way we are loading SVGs vs current but at least we found a solution that is working great. Do you know when the code you have set to merge will be available in the main branch? I can probably push this story off a few weeks if your PR will be available sometime soon. Id also like so say thanks again for taking the time to respond so much.

@asturur
Copy link
Member

asturur commented Jan 16, 2017

Well i have to clear separated text issues first and will take some time. The code will be similar and you should have no problem upgrading since the properties will stay same ( scaleX, scaleY )
Is weird anyway to have a pattern not to scale on canvas zoom. That is the reason why you have to setup it manually with updating scaleX and scaleY while for direct zoom there is a scaleWithObject property.

Yes this build of fabric was integrating also SVG changes to parse real patterns from svgs.
Because the things was getting bigger and bigger i stopped it and wanted to commit the things separately.
You should join all the changes but not the one from parser.js and element_parsers.js, you can do easily if you clone the repository on the disk and use git checkout ...filename....

@dvideby0
Copy link
Author

I was able to fork and merge using your changes on all files except the 2 you mentioned. If you want to check it out, https://github.com/dvideby0/352-fabric.js SVG issue has been resolved.

@alok-sin
Copy link

This might be too late but one approach can be to set the pattern as the canvas background color.
https://stackoverflow.com/questions/24156592/how-to-set-repeating-background-image-for-a-canvas-in-fabric-js

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

3 participants