吹き出しクラス tatsuya forked:5favorite:11lines:178license : All rights reserved modified : 2009-04-28 14:07:43 Embed Tweet package { import flash.display.Sprite; [SWF(width=465, height=465, backgroundColor=0xFFFFFF, frameRate=30)] public class FlashTest extends Sprite { public function FlashTest() { var textStr1:String = "あんなの飾りです。偉い人にはそれが分からんのですよ。\n(通常モード)"; var textStr2:String = "あんなの飾りです。偉い人にはそれが分からんのですよ。\n(debugモード)"; var fukidashi1:Fukidashi = new Fukidashi(stage, 70 , 250 , 150 , textStr1);//通常モードでオブジェクト作成 var fukidashi2:Fukidashi = new Fukidashi(stage, 270 , 250 , 150 , textStr2, true);//debugモードでオブジェクト作成 } } } import flash.accessibility.AccessibilityProperties; import flash.display.*; import flash.events.*; import flash.geom.Point; import flash.text.*; internal class Fukidashi { public var bottomSheet:Sprite;//このクラスで描画するのを乗せるSprite public const REP_LENGTH:Number=2;//コントロールポイントの位置を決めるための代表値 public const PADDING:Number=5; public const TAIL_POSITION:uint = 5;//この番号のラインの途中に尻尾を作成(変えるとだめ!) public const TAIL_POSITION_RATE:Number = 0.5;//ベジェ曲線のtがこの値を中心として尻尾が生える public const TAIL_WIDTH_RATE:Number = 0.04;//TAIL_POSITION_RATE-TAIL_WIDTH_RATE ~ TAIL_POSITION_RATE+TAIL_WIDTH_RATE で尻尾の根元 public var commentTF:TextField;//文字列はこのテキストフィールドで表示 public function Fukidashi(st:Object , tailX0:Number , tailY0:Number, w0:Number , textStr:String , fDebug:Boolean=false) {// コンストラクタ var ii:uint; var h0:Number;//文字列を表示する高さ(横幅w0は引数で受け、文字数によりh0を後で決めてます) //bottomSheetの作成 bottomSheet = new Sprite(); st.addChild(bottomSheet); bottomSheet.visible = true;//デフォルトは表示 if (fDebug) { var debugSheet:Sprite = new Sprite();//デバッグ用の点などはここに描きます } //デバッグ時のみtailX0 , tailY0の表示 if (fDebug) { var tailTopSpr:Sprite = new Sprite(); tailTopSpr.graphics.beginFill(0xFF0000); tailTopSpr.graphics.drawCircle(tailX0, tailY0, 3); tailTopSpr.graphics.endFill(); st.addChild(tailTopSpr); } //テキストフィールド設定 commentTF= new TextField(); commentTF.text = textStr; commentTF.width = w0 + 4; commentTF.multiline = true; commentTF.wordWrap = true; h0 = commentTF.textHeight + 4;//ここでテキストフィールドの高さを決めてます commentTF.height = h0; if (fDebug) { //テキストフィールドのデバッグ用表示 var displayArea:Sprite = new Sprite(); displayArea.graphics.beginFill(0xEEEEEE); displayArea.graphics.drawRect(0, 0, w0, h0); displayArea.graphics.endFill(); debugSheet.addChild(displayArea); //PADDINGを考慮した外枠の表示(ここにアンカーポイントが乗ります) var displayFrame:Sprite = new Sprite(); displayFrame.graphics.lineStyle(0, 0xAAAAAA); displayFrame.graphics.drawRect(-PADDING, -PADDING, w0+2*PADDING, h0+2*PADDING); debugSheet.addChild(displayFrame); } //アンカーポイント var anchorPoint:Array = new Array(); anchorPoint.push(new Point(w0 *0.11, -PADDING ) ) ; anchorPoint.push(new Point(w0 *0.675, -PADDING ) ) ; anchorPoint.push(new Point(w0 + PADDING , h0 *0.21)); anchorPoint.push(new Point(w0 + PADDING , h0 *0.9)); anchorPoint.push(new Point(w0 *0.825, h0 + PADDING ) ) ; anchorPoint.push(new Point(w0 *0.36, h0 + PADDING ) ) ; anchorPoint.push(new Point(-PADDING , h0 *0.85)); anchorPoint.push(new Point( -PADDING , h0 * 0.25)); var nAnchorPoint:int = anchorPoint.length;//アンカーポイントの数 if (fDebug) {//アンカーポイントのデバッグ用表示 for (ii = 0; ii < nAnchorPoint; ii++) drawDebugDot(anchorPoint[ii].x , anchorPoint[ii].y , 0x0000CC); } //コントロールポイントの位置を決める係数 //※コントロールポイント座標はアンカーポイントからの相対座標で、この係数とREP_LENGTHから決まります var keisuuArr:Array = new Array(); keisuuArr.push( { x:1.6 , y:-2.4 } ); keisuuArr.push( { x:-2.4 , y:-2 } ); keisuuArr.push( { x:8.2 , y:-2 } ); keisuuArr.push( { x:1.8 , y:-5.6 } ); keisuuArr.push( { x:1.6 , y:2.8 } ); keisuuArr.push( { x:1.4 , y:-2.6 } ); keisuuArr.push( { x:0.8 , y:3.6 } ); keisuuArr.push( { x:3.8 , y:1.2 } ); keisuuArr.push( { x:-3.2 , y:1.8 } ); keisuuArr.push( { x:3.4 , y:2 } ); keisuuArr.push( { x:-5.4 , y:2.4 } ); keisuuArr.push( { x:-1.6 , y:5 } ); keisuuArr.push( { x:-1.4 , y:-2 } ); keisuuArr.push( { x:-1.6 , y:2.4 } ); keisuuArr.push( { x:-1 , y:-2.4 } ); keisuuArr.push( { x: -5.2 , y: -0.8 } ); //コントロールポイント var controlPoint:Array = new Array(); //コントロールポイント座標計算 var ite1:int, ite2:int, xx:Number, yy:Number; for (ii = 0; ii < nAnchorPoint; ii++) { ite2 = ii * 2; ite1 = (ite2 - 1 == -1) ? 15 : ite2 - 1; xx = REP_LENGTH * keisuuArr[ite1].x; yy = REP_LENGTH * keisuuArr[ite1].y; controlPoint.push(new Point(xx, yy)); xx = REP_LENGTH * keisuuArr[ite2].x; yy = REP_LENGTH * keisuuArr[ite2].y; controlPoint.push(new Point(xx, yy)); } var nControlPoint:int = controlPoint.length;//コントロールポイントの数 //ベジェ曲線を描く //※輪郭は3次ベジェ曲線を直線をつないで表示、尻尾部分は2次ベジェ曲線(curveToメソッド)で表示してます var tailTopPt:Point;//尻尾の先端の座標 var p0:Point, p1:Point, p2:Point, p3:Point , t:Number, pt:Point , dt:Number=0.02; var nextnn:uint; var bezierCurveSpr:Sprite = new Sprite();//ベジェ曲線はここに乗せます bottomSheet.addChild(bezierCurveSpr); bezierCurveSpr.graphics.beginFill(0xFFFFFF);//吹き出し内部の色 bezierCurveSpr.graphics.lineStyle(0, 0x333333);//吹き出しの線の幅と色 for (ii = 0; ii < nAnchorPoint; ii++) { drawBezierCurve(ii); } bezierCurveSpr.graphics.endFill(); function drawBezierCurve(nn:uint):void {//ベジェ曲線を描く p0 = new Point(anchorPoint[nn].x , anchorPoint[nn].y);//アンカーポイント nextnn = (nn + 1 == nAnchorPoint) ? 0 : nn + 1;//最後だと次のnnは0になる p3 = new Point(anchorPoint[nextnn].x , anchorPoint[nextnn].y);//アンカーポイント ite1 = nn * 2 + 1; ite2 = (ite1 + 1 == nControlPoint) ? 0 : ite1 + 1; p1 = new Point(p0.x+controlPoint[ite1].x , p0.y+controlPoint[ite1].y);//コントロールポイント p2 = new Point(p3.x+controlPoint[ite2].x , p3.y+controlPoint[ite2].y);//コントロールポイント if (fDebug) {//デバッグ時のコントロールポイントの表示 drawDebugDot(p1.x , p1.y , 0x990000); drawDebugDot(p2.x , p2.y , 0x990000); } if (nn==0) bezierCurveSpr.graphics.moveTo(p0.x, p0.y);//最初だけmoveするけどそれ以降は不要 for (t = 0.0; t <= 1.0; t += dt) { if ( (nn == TAIL_POSITION) && (TAIL_POSITION_RATE - TAIL_WIDTH_RATE < t) && (t < TAIL_POSITION_RATE + TAIL_WIDTH_RATE) ) {//尻尾の根元の部分か? if ( (TAIL_POSITION_RATE-dt/2 < t)&&(t < TAIL_POSITION_RATE+dt/2) ) { //↑t==TAIL_POSITION_RATEで評価すると誤差のせいかtrueにならないことがあったのでこんな風にしてます //↑がtrueの場合のみ1回だけ↓の関数をCALLして尻尾を描きます drawTail(getBezierPoint(TAIL_POSITION_RATE-TAIL_WIDTH_RATE),getBezierPoint(t),getBezierPoint(TAIL_POSITION_RATE+TAIL_WIDTH_RATE)); } }else{//通常こちらで輪郭を描く pt = getBezierPoint(t); bezierCurveSpr.graphics.lineTo(pt.x, pt.y); } } bezierCurveSpr.graphics.lineTo(p3.x, p3.y); } function drawTail(tailPtA:Point, tailPtC:Point, tailPtB:Point):void {//尻尾部分の描画(ここは1回だけCALLされます) //ここは2次のベジェ曲線(curveToメソッド)で描く tailTopPt= new Point(tailPtC.x - 17 , tailPtC.y + 20);//尻尾の先っぽの位置をこのようにして決めてます //第1の線 drawATailCurve(1, tailPtA, tailTopPt); //第2の線 drawATailCurve(2, tailTopPt, tailPtB); function drawATailCurve(curveNumber:uint, p0:Point , p1:Point):void {//1つの2次ベジェ曲線を描きます //p0,p1はアンカーポイント var pCent:Point;//2つのアンカーポイントの中点 var pCont:Point;//コントロールポイント pCent = new Point( (p0.x + p1.x) / 2 , (p0.y + p1.y) / 2); //コントロールポイントは中点を通り、アンカーポイントを結ぶ直線と直交する直線上にあるように計算 var tpt1:Point , tpt2:Point, rad:Number = 10 * Math.PI / 180;//この角度を小さくするほど直線に近くなる if (curveNumber==2) rad *= -1; var xl1:Number = p0.x - pCent.x; var yl1:Number = p0.y - pCent.y; tpt1 = new Point(pCent.x + Math.cos(rad) * xl1 - Math.sin(rad) * yl1 , pCent.y + Math.sin(rad) * xl1 + Math.cos(rad) * yl1); rad *= -1; xl1 = p1.x - pCent.x; yl1 = p1.y - pCent.y; tpt2 = new Point(pCent.x + Math.cos(rad) * xl1 - Math.sin(rad) * yl1 , pCent.y + Math.sin(rad) * xl1 + Math.cos(rad) * yl1); pCont = new Point( (tpt1.x + tpt2.x) / 2 , (tpt1.y + tpt2.y) / 2); bezierCurveSpr.graphics.curveTo(pCont.x , pCont.y , p1.x , p1.y);//2次ベジェ曲線 } } function getBezierPoint(t:Number):Point {//3次ベジェ曲線を求める式 return new Point(Math.pow(1 - t, 3) * p0.x + 3 * t * Math.pow(1 - t, 2) * p1.x + 3 * t * t * (1 - t) * p2.x + t * t * t * p3.x , Math.pow(1 - t, 3) * p0.y + 3 * t * Math.pow(1 - t, 2) * p1.y + 3 * t * t * (1 - t) * p2.y + t * t * t * p3.y); } //位置調整(引数の座標に尻尾の先が来るようにbottomSheetを移動) bottomSheet.x = tailX0-tailTopPt.x; bottomSheet.y = tailY0-tailTopPt.y; //デバッグ時のみ if (fDebug) bottomSheet.addChild(debugSheet); //テキストフィールド bottomSheet.addChild(commentTF);//ベジェ曲線より後じゃないとだめなのでここでaddChildしてます function drawDebugDot(xx:Number , yy:Number , color:Number):void {//デバッグ用の点の表示 var dot:Sprite = new Sprite(); dot.graphics.beginFill(color); dot.graphics.drawCircle(xx, yy, 3); dot.graphics.endFill(); debugSheet.addChild(dot); } }////////////////////////////////////////////////////////////////////////////////コンストラクタ //////////////////////////////////////////////////////////////////////////////// public function set visible(f:Boolean):void { bottomSheet.visible = f; }//////////////////////////////////////////////////////////////////////////////// } Code Fullscreen Preview Fullscreen siouxcitizen.. _Andros_ sw_lucchini kidaipu inamiy ikedahidenor.. civet demouth hokori : まんがふきだし nijitaro : 吹き出し clockmaker : ふきだし 吹き出し push Point Math.pow stage Math.cos wordWrap visible textHeight curveTo color lineTo drawRect addChild Math.sin drawCircle Boolean endFill multiline beginFill Array sort new page view favorite forked pv463 forked from: 吹き出しクラス meat18 forked:0 favorite:0lines:178 (diff:3) pv1847 forked from: 吹き出しクラス kidaipu forked:3 favorite:2lines:61 (diff:283) pv516 forked from: 吹き出しクラス inamiy forked:0 favorite:0lines:178 (diff:3) pv299 forked from: 吹き出しクラス diego forked:0 favorite:0lines:178 (diff:1) pv625 forked from: 吹き出しクラス ezshine forked:0 favorite:2lines:178 (diff:2) tag: TEXT