When I use <s:Rect> to create a rectangle, I use radiusX to get rounded corners. Problem is all are the same roundedness. Is there something similar to Rect that lets me control the radius for each corner separately? If not, what's the best way to create this from scratch? graphics library or what?
You can do this with a Rect by setting specific values for the topRightRadiusX, topLeftRadiusX, bottomRightRadiusX and bottomLeftRadiusX properties rather than setting radiusX.
public var rectangle:Shape=new Shape();
public var temp:int=1;
public var ui:UIComponent=new UIComponent();
public var i:int=new int;
public var j:int=new int;
public var n:int=0;
public function init(event:Event):void
{
ui.addChild(rectangle);
myCanvas.addChild(ui);
rectangle.graphics.lineStyle(4,0x0000FF,1,false,"normal","none","bevel",9);
rectangle.graphics.drawRoundRect(20,20,200,200,0,0);
}
private function changeSize():void
{
if(temp>=1)
{
rectangle.graphics.clear();
}
rectangle.graphics.lineStyle(4,0x0000FF,1,false,"normal","none","bevel",9);
rectangle.graphics.drawRoundRect(20,20,200,200,hSlider.value,hSlider.value);
}
]]>
</mx:Script>
<mx:Canvas height="100%" width="100%" id="myCanvas">
<mx:HSlider id="hSlider" minimum="0" maximum="170" value="0"
dataTipPlacement="top"
snapInterval="1" tickInterval="1"
labels="['0%','100%']"
liveDragging="true"
change="changeSize();" x="25" y="233"/>
</mx:Canvas>
You might want to check out the StyledBox component here: http://carrythezero.net/blog/2009/06/01/flex-rounding-specific-corners-on-a-box/
It extends box and you can specify through CSS which corners you want rounded.
Related
I have 2 canvas objects that both contain a tiling image. My client recently requested that both the images be able to appear at the same time. I simply allowed them to be both enabled at the same time thinking they would tile appropriately but they do not.
After some careful thought I realized this is because they are of varying width so although my tiling image is 7 pixels in width, it may not always end at 7 pixels thus causing it to not appear to tile cleanly.
I can't just lop off the remainders because the imagery is being used to assess quantities of items (without going into too much detail) and having item 1 with 97 quantity next to item 2 with 200 quantity on one row compared to row 2 with item 1 having 100 quantity and item 2 having 300 quantity will show up strange to the end-user.
Does anyone know of how I could get started with maybe splicing the two canvas objects together or rather using 1 canvas object and then using BOTH background images and setting a percentWidth or something that the other one comes into affect?
You can use the copyPixels() method of the BitmapData class to achieve this sort of result. To do this, you need to load each image with a Loader. When the images are loaded, you have two Bitmap objects. From there, you access the corresponding BitmapData for each Bitmap and can splice them together.
After the splice, you create a new Bitmap object from the combined BitmapData. Then you feed the combined Bitmap to some other component. Unfortunately, a Canvas won't accept a Bitmap for its backgroundImage style. But you can add the combined Bitmap to a UIComponent or set it as the source of an Image component.
Finally, add either the UIComponent or Image to a Canvas. Since you can layer things on the Canvas, you effectively have a background image as long as place (and leave) the combined image at index 0 of the Canvas.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical"
minWidth="955" minHeight="600"
creationComplete="onCreationComplete()">
<mx:Script>
<![CDATA[
import mx.containers.Canvas;
import mx.controls.Image;
import mx.core.UIComponent;
import mx.events.FlexEvent;
private var loader1:Loader;
private var loader2:Loader;
private var bitmap1:BitmapData;
private var bitmap2:BitmapData;
protected function onCreationComplete():void
{
loader1 = new Loader();
loader1.load(new URLRequest("Android_Robot_100.png"));
loader1.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoader1Complete, false,0,true);
loader2 = new Loader();
loader2.load(new URLRequest("rails.png"));
loader2.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoader2Complete, false,0,true);
}
protected function onLoader1Complete(event:Event):void
{
bitmap1 = (loader1.content as Bitmap).bitmapData;
if (bitmap1 && bitmap2)
copyPixels();
}
private function onLoader2Complete(event:Event):void
{
bitmap2 = (loader2.content as Bitmap).bitmapData;
if (bitmap1 && bitmap2)
copyPixels();
}
protected function copyPixels():void
{
// false = non-transparent background
var combined:BitmapData = new BitmapData(200,200, false);
var sourceRect1:Rectangle = new Rectangle(0,0,bitmap1.width, bitmap1.height);
var sourceRect2:Rectangle = new Rectangle(0,0,bitmap2.width, bitmap2.height);
combined.copyPixels(bitmap1, sourceRect1, new Point(0,0));
combined.copyPixels(bitmap2, sourceRect2, new Point(bitmap1.width, 0));
var result:Bitmap = new Bitmap(combined);
var image:Image = new Image();
image.source = result;
var canvas:Canvas = new Canvas();
canvas.addChildAt(image, 0);
addChild(canvas);
// as an alternative, just add the bitmap to a UIComponent
// note you can't do this and the above at the same time,
// becuase the 'result` Bitmap can only be the child of one
// object, adding it to one thing removes it from the other...
// var uic:UIComponent = new UIComponent();
// uic.addChild(result);
// addChild(uic);
}
]]>
</mx:Script>
</mx:Application>
Ultimately I was overcomplicating it. I simply did this with masking like...
<mx:Canvas width="100%" height="100%">
<mx:Image id="myFirstUnrelatedImage" width="50%" height="65%" maintainAspectRatio="false"
verticalCenter="0" left="3"
source="#Embed('../assets/image1.png')" visible="true"/>
<!-- not sure why borderStyle here is required but it won't show up without? -->
<mx:Canvas id="image1mask" width="30%" height="94%"
borderStyle="none" visible="false"/>
<mx:Canvas id="image1" width="100%" height="100%"
minWidth="0" styleName="myStyle"
mask="{image1mask}"/>
<!-- not sure why borderStyle here is required but it won't show up without? -->
<mx:Canvas id="image2mask" width="20%" height="94%"
right="0" borderStyle="none" visible="false"
includeInLayout="false"/>
<mx:Canvas id="image2" width="100%" height="100%"
minWidth="0" styleName="myStyle2"
mask="{image2mask}"/>
</mx:Canvas>
Where my styles were controlling the backgroundImage and backgroundRepeat properties in css.
anybody know how to make a custom hslider in Flex 4 (spark) with two thumbs? Since Flex 4 the thumbcount property of the slider component isn't longer available (at the mx component it was easily to set). I have to style the track and the thumbs.
A tutorial would be nice.
thx,
tux.
I don't have a full tutorial for you but here are the first few steps in creating a custom hslider component. Hope it helps.
Start by looking at the hslider skin which is made up of 2 parts, a thumb and a track:
<s:Button id="track" left="0" right="0" top="0" bottom="0" minWidth="33" width="100"
skinClass="spark.skins.spark.HSliderTrackSkin" />
<s:Button id="thumb" top="0" bottom="0" width="11" height="11"
skinClass="spark.skins.spark.HSliderThumbSkin" />
Now, create a new skin except give it two buttons:
<s:Button id="track" left="0" right="0" top="0" bottom="0" minWidth="33" width="100"
skinClass="spark.skins.spark.HSliderTrackSkin" />
<s:Button id="thumb" top="0" bottom="0" width="11" height="11"
skinClass="spark.skins.spark.HSliderThumbSkin" />
<s:Button id="thumb2" top="0" bottom="0" width="11" height="11"
skinClass="spark.skins.spark.HSliderThumbSkin" />
Create a new component that extends HSlider and call it something like MultiButtonSlider. Override the partAdded() function and grab a reference to thumb2 when its added.
override protected function partAdded(partName:String, instance:Object):void{
if(partName == "thumb2"){
thumb2 = instance as Button;
}
}
Hope this starts you off in the right direction. Don't forget to set the MultiButtonSlider.skinClass = "YourNewSkin"
Next steps would be to make it draggable and convert its point to a value. See the HSlider.pointToValue() function.
Patrick Mowrer has a free one over on GitHub: https://github.com/pmowrer/spark-components
I was able to use it without much of a problem in a recent project. The component doesn't expose (to MXML) all the properties that the Spark one does (for example, dataTipFormatFunction is absent), but one can still access and customize them through custom skinning.
I had the same problem. I'm using the mx component instead of the sparks compontent for now.
<mx:HSlider x="46" y="358" minimum="1" maximum="600" snapInterval="1"
thumbCount="2" values="[1,600]" id="hsTiming" height="23" width="618"
change="hsTiming_changeHandler(event)"/>
You can take a look at this topic (AS3)
Flash Range Slider Component
To supplement shi11i's answer, who put me on the right track, here is the full code :
package test.components
{
import flash.geom.Point;
import spark.components.Button;
import spark.components.Group;
import spark.components.HSlider;
public class HSliderTwoThumbs extends HSlider
{
private var _value2:Number;
[Bindable]
public function get value2():Number
{
return _value2;
}
public function set value2(value:Number):void
{
_value2=value;
invalidateDisplayList();
}
[SkinPart(required="true")]
public var thumb2:Button;
public function HSliderTwoThumbs()
{
super();
//this.setStyle("skinClass", "HRangeSliderSkin");
}
override protected function partAdded(partName:String, instance:Object):void
{
super.partAdded(partName, instance);
}
override protected function updateSkinDisplayList():void
{
super.updateSkinDisplayList();
if (!thumb2 || !track || !rangeDisplay)
return;
var thumbRange:Number=track.getLayoutBoundsWidth() - thumb2.getLayoutBoundsWidth();
var range:Number=maximum - minimum;
// calculate new thumb position.
var thumbPosTrackX:Number=(range > 0) ? ((value2 - minimum) / range) * thumbRange : 0;
// convert to parent's coordinates.
var thumbPos:Point=track.localToGlobal(new Point(thumbPosTrackX, 0));
var thumbPosParentX:Number=thumb2.parent.globalToLocal(thumbPos).x; //- distanceToSecondThumb
thumb2.setLayoutBoundsPosition(Math.round(thumbPosParentX), thumb2.getLayoutBoundsY());
}
}}
And here is how to use it :
<components:HSliderTwoThumbs id="sliderTwoThumbs" skinClass="test.skins.HRangeSliderSkin"
width="300"
minimum="0"
maximum="300"
value="150"
value2="100"
/>
Hope this helps.
Note : In my case I did not handle the draggability of the second cursor, as I did not not need it (it was a "read-only" component). I would be interested in seeing how you handle it, though.
I need to move a sprite only vertically on mouse move. How do I implement it with as3?
Thanks
Flash version
var s:Sprite = new Sprite();
s.x = 20;
s.graphics.beginFill(0xFF0000);
s.graphics.drawRect(0,0,20,20);
addChild(s);
stage.addEventListener(MouseEvent.MOUSE_MOVE,moveSprite);
function moveSprite(e:MouseEvent):void
{
s.y = e.localY;
}
Flex version
<mx:Canvas width="100" height="100">
<mx:mouseMove>
<![CDATA[
s.y = event.localY;
]]>
</mx:mouseMove>
<mx:Canvas id="s" backgroundColor="#ff0000" width="20" height="20"/>
</mx:Canvas>
Each of these you can paste in and will do what you said. it will create a 20x20 red box that is vertically the same as the mouse but fixed horizontally. The flex version your mouse has to be within the containing Canvas.
addEventListener(MouseEvent.CLICK, clickHandler);
function clickHandler(e:MouseEvent):void{
mySprite.y += amount;
}
Ok, dragging is a little more complicated. You need to define a rectangle for the bounds of the dragging. If you want to just drag along one axis then you make the rectangle have a width of 0. In this example I've restricted the amount of scrolling and and down to different numbers that you can change below.
import flash.events.MouseEvent;
import flash.geom.Rectangle;
mySprite.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
function mouseDownHandler(event:MouseEvent):void{
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
var scrollUpAmount:int = 10;
var scrollDownAmount:int = 200;
var boundsRect:Rectangle = new Rectangle(mySprite.x,mySprite.y-scrollUpAmount,0,mySprite.y+scrollDownAmount);
mySprite.startDrag(false, boundsRect);
}
function mouseUpHandler(event:MouseEvent):void{
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
mySprite.stopDrag();
}
Please, anyone can guide me how to do this stuff.
I have a pannel and a box, The pannel is my component pannel(inside component pannel is eg. image, TextArea, Video) and
Box is my target Box for my component creation. The user can select a component he/she want to create (dynamic creation)
and drag it to target Box(drag and drop event). Then after creating the component
the user can drag the component that he/she created and place anywhere on target
Box and also the user can resize the component that he/she created(runtime resize).
i have this code for drag and drop and it seems this code works only on image
//-----action script-----//
private function dragIt(event:MouseEvent, value: String, objParent: String, objName: String):void
{
var dragInitiator:Image = event.currentTarget as Image;
var dragSource:DragSource = new DragSource();
dragSource.addData(value,'value');
dragSource.addData(objParent, 'parent');
dragSource.addData(objName, 'objname');
var dragProxy:Image = new Image();
dragProxy.source = event.currentTarget.source;
DragManager.doDrag(dragInitiator, dragSource, event, dragProxy);
}
private function dragEnterHandler(event:DragEvent):void
{
var dropTarget:Box=event.currentTarget as Box;
dropTarget.setStyle("borderThickness", 5);
DragManager.acceptDragDrop(dropTarget);
}
private function dragExitHandler(event:DragEvent):void
{
var dropTarget:Box=event.currentTarget as Box;
revertBoxBorder();
}
private function revertBoxBorder():void
{
targetBox.setStyle("borderThickness", 1);
}
private function dragDropHandler(event:DragEvent):void
{
var value:String = event.dragSource.dataForFormat('value') as String;
var objParent:String = event.dragSource.dataForFormat('parent') as String;
if(value == "mp3")
{
//do something
}
else if (value == "image")
{
if (objParent == "panel")
{
var imgView: Image = new Image;
imgView.x = event.stageX;
imgView.y = event.stageY;
addChild(imgView);
imgView.name = String(getChildByName(imgView.name).parent.numChildren-1);
imgView.addEventListener(MouseEvent.MOUSE_MOVE, function(e:MouseEvent):void
{
dragIt(e, value, 'box', Image(e.target).name);
});
imgView.source = ImgInsert;
}
else
{
var objName:String = event.dragSource.dataForFormat('objname') as String;
getChildByName(objName).parent.getChildAt(int(objName)).x = event.stageX;
getChildByName(objName).parent.getChildAt(int(objName)).y = event.stageY;
}
}
else if (value == "textarea")
{
//do something
}
}
//-----mxml code------//
<mx:Panel x="0" y="37" width="91" height="417" layout="absolute" title="Component" borderColor="#8DA5AB" color="#345860" borderStyle="outset">
<mx:Image x="7" y="43" width="21" height="18" source="{TxtAreaInsert}" mouseMove="dragIt(event,'textarea','panel','')"/>
<mx:Image x="36" y="43" width="21" height="18" source="{ImgInsert}" mouseMove="dragIt(event,'image','panel','')"/>
<mx:Image x="36" y="80" width="21" height="18" source="{Mp3Insert}" mouseMove="dragIt(event,'mp3','panel','')"/>
<mx:Image x="7" y="80" width="21" height="18" source="{VdoInsert}" mouseMove="dragIt(event,'video','panel','')"/>
</mx:Panel>
<mx:Box id="targetBox" y="37" width="589" height="417" borderColor="#8CC2E8" backgroundColor="#D5DBEE"
dragExit="dragExitHandler(event)" dragEnter="dragEnterHandler(event)" dragDrop="dragDropHandler(event)" left="99">
</mx:Box>;
How to move thus non image component like TxtArea? How to resize the component inside target box?
(This is like GUI of flex when creating component)
Thank you..
check out Rogue-Development.com's Object Handles I've used this with pretty good success for moving / resizing components.
Also try out the Pantaste library which is a lot more sophisticated than Object Handles.
Goto http://sourceforge.net/projects/tcycomponents/
and download package and demo if you want.
Use TcyReziser component for easy moving/resizing like Delphi 2009 does!
Regards,
Mauricio
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!
<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>
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.
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.
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();
}
}
Looks like this might be useful:
http://forums.adobe.com/message/199071#199071
Set ClipToBounds property of the Canvas to true:
<Canvas ClipToBounds="True">... Content ...</Canvas>