user12163
user12163

Reputation:

How to make the Canvas clip its contents in Flex?

I draw a line on a Canvas object with the moveTo and lineTo graphics methods. If one end of the line lies outside the Canvas, the line spills out and is drawn over or under other elements in the application.

How do I make the Canvas keep the line contained within itself?

Thanks!

Upvotes: 6

Views: 5353

Answers (6)

Reputation:

Looks like this might be useful:

http://forums.adobe.com/message/199071#199071

Upvotes: 0

Thomas Auinger
Thomas Auinger

Reputation: 2095

I have just developed a Flex Box component, which acts as a regular component container, but draws a rounded rectangle background, with another rounded rectangle indicated a fill-level. For that I needed to clip the upper section that should not get filled. Drawing the fill rectangle to the fill height was no option since the rounded corners would not match.

What I learned:

  • I created a Canvas component just for drawing the fill-level with bounds 0/0 and width/height of the Box
  • I added that canvas to the Box at index 0 via addChildAt()
  • I set the includeInLayout property to false for that canvas since it should not take part in the layouting of the Box itself but rather act as some floating drawing pane on top
  • I then added another Canvas as the mask to that fill-canvas (addChild(), and set mask property)

Here is some code:

override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
    // super
    super.updateDisplayList(unscaledWidth, unscaledHeight);

    // prep
    var g:Graphics = this.graphics;
    var fgColor:int = this.getStyle("fillColor");
    var bgColor:int = this.getStyle("backgroundFillColor");
    var radius:int = this.getStyle("cornerRadius");

    // clear
    g.clear();

    // draw background
    g.beginFill(bgColor, 1);
    g.drawRoundRect(0, 0, unscaledWidth, unscaledHeight, radius, radius);
    g.endFill();

    // draw fill level
    if (this._fillLevel > 0) {
        var fillHeight:int = int(unscaledHeight * this._fillLevel);

        // extra component for drawing fill-level, so we can apply mask just to this
        if (this._fillLevelCanvas == null) {
            this._fillLevelCanvas = new Canvas();
            this._fillLevelCanvas.x = 0;
            this._fillLevelCanvas.y = 0;
            this._fillLevelCanvas.includeInLayout = false;
            this.addChildAt(this._fillLevelCanvas, 0);
        }
        this._fillLevelCanvas.width = unscaledWidth;
        this._fillLevelCanvas.height = unscaledHeight;

        // mask
        if (this._fillLevelMask == null) {
            this._fillLevelMask = new Canvas();
            this._fillLevelMask.x = 0;
            this._fillLevelMask.y = 0;
            this._fillLevelCanvas.addChild(this._fillLevelMask);
            this._fillLevelCanvas.mask = this._fillLevelMask;
        }
        this._fillLevelMask.width = this.width;
        this._fillLevelMask.height = this.height;
        this._fillLevelMask.graphics.beginFill(0xFFFFFF); 
        this._fillLevelMask.graphics.drawRect(0, this.height-fillHeight, this._fillLevelMask.width, this._fillLevelMask.height); 
        this._fillLevelMask.graphics.endFill();                 

        this._fillLevelCanvas.graphics.beginFill(fgColor, 1);
        this._fillLevelCanvas.graphics.drawRoundRect(0, 0, unscaledWidth, unscaledHeight, radius, radius);
        this._fillLevelCanvas.graphics.endFill();
    }
}

Upvotes: 0

David Pond
David Pond

Reputation: 1125

The link in the recommended answer is broken. I solved the problem by placing another canvas inside my canvas that is larger than the outer canvas.

Example:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" applicationComplete="onComplete()">
    <mx:Script><![CDATA[
        private function onComplete():void
        {
            canvas.graphics.lineStyle(1);
            canvas.graphics.moveTo( -100, -100);
            canvas.graphics.lineTo(400, 400);
        }
    ]]></mx:Script>
    <mx:Canvas  id="window"
                height="300" 
                width="300" 
                clipContent="true" 
                horizontalScrollPolicy="off" 
                verticalScrollPolicy="off" 
                backgroundColor="white">
        <mx:Canvas id="canvas" width="301" height="301">
        </mx:Canvas>
    </mx:Canvas>
</mx:Application>

If the window Canvas is going to be resized at runtime, add a resize event listener to resize the canvas Canvas also.

Upvotes: 0

Set ClipToBounds property of the Canvas to true:

<Canvas ClipToBounds="True">... Content ...</Canvas>

Upvotes: -1

philip andrew
philip andrew

Reputation:

<mx:Canvas id="canvas" top="0" right="51" left="0" bottom="32">
<mx:Canvas x="-1" y="0" width="0" height="0"> <!-- This is a HACK to make the canvas clip -->
</mx:Canvas>
</mx:Canvas>

Upvotes: 2

seanhodges
seanhodges

Reputation: 17524

I had a similar problem some time ago. You need to embed another container inside the canvas, and draw the primitive graphics in that instead. I believe this is because the Canvas component only clips child components, and not primitive graphics.

Example here: http://www.adobe.com/cfusion/webforums/forum/messageview.cfm?forumid=60&catid=585&threadid=1421196. It includes some sample code about half way down the page.

Upvotes: 2

Related Questions