/* ************************************************ EXAMPLE: Hello World AUTHOR: Jon Williams CREATED: April 30, 2005 MODIFIED: April 30, 2005 ASBE == Great Software Examples http://www.ifbin.com/asbe ************************************************ */ class toolTip { private var mc; public var innerTail public var outerTail public function toolTip(context, text, style) { //draw a cartoon bubble style tooltip using a dynamic textfield var tf, bg, border, outerOffset; var innerTailPt, outerTailPt, name; var outerBoxH, outerBoxW, innerBoxW, innerBoxH; var depth; //make a container depth = context.getNextHighestDepth(); name = "toolTip" + depth; context.createEmptyMovieClip(name, depth); mc = context[name]; //create a movieClip for the border depth = mc.getNextHighestDepth(); name = "border"; //create a movieClip for the background mc.createEmptyMovieClip(name, depth); depth = mc.getNextHighestDepth(); name = "bg"; //create a movieClip for the textfield mc.createEmptyMovieClip(name, depth); depth = mc.getNextHighestDepth(); name = "tf"; mc.createTextField(name, depth, 0, 0, 0, 0); tf = mc.tf; tf.autoSize = true; tf.embedFonts = true; tf.html = true; tf.selectable = false; tf.multiline = true; tf.htmlText = "" + text + ""; //draw the background outerBoxW = tf._width + (2 * style.padding) + (2 * style.tailLength); outerBoxH = tf._height + (2 * style.padding) + (2 * style.tailLength); outerTail = getCoordOnRectFromAngle(outerBoxW, outerBoxH, style.tailAngle, Math.min(style.cornerRadius, style.cornerRadius + style.tailRadius)); outerTail.x = outerTail.x + (tf._width / 2); outerTail.y = outerTail.y + (tf._height / 2); innerBoxW = tf._width + (2 * style.padding) - (2 * style.tailRadius); innerBoxH = tf._height + (2 * style.padding) - (2 * style.tailRadius); innerTail = getCoordOnRectFromAngle(innerBoxW, innerBoxH, style.tailAngle, Math.max(0, style.cornerRadius - style.tailRadius)); innerTail.x = innerTail.x + (tf._width / 2); innerTail.y = innerTail.y + (tf._height / 2); innerTailPt = getTangentPts(outerTail.x, outerTail.y, innerTail.x, innerTail.y, style.tailRadius, style.tailAngle); bg = mc.bg; if( _root.debug ) { bg.beginFill(style.bgColor, 20); } else bg.beginFill(style.bgColor); drawRect(bg, -style.padding, -style.padding, tf._width + (2 * style.padding), tf._height + (2 * style.padding), style.cornerRadius); bg.endFill(); if( _root.debug ) { bg.beginFill(0xffcc00); drawOval(bg, innerTail.x, innerTail.y, style.tailRadius); bg.endFill(); } if( _root.debug ) { bg.beginFill(0xffcc00, 20); } else bg.beginFill(style.bgColor); bg.moveTo(outerTail.x, outerTail.y); bg.lineTo(innerTailPt[0].x, innerTailPt[0].y); bg.lineTo(innerTailPt[1].x, innerTailPt[1].y); bg.endFill(); //draw the border border = mc.border; outerOffset = style.padding + style.borderWidth; outerBoxW = tf._width + (2 * style.padding) + (2 * ((style.tailRadius + style.borderWidth) * style.tailLength / style.tailRadius)) + (2 * style.borderWidth); outerBoxH = tf._height + (2 * style.padding) + (2 * ((style.tailRadius + style.borderWidth) * style.tailLength / style.tailRadius)) + (2 * style.borderWidth); outerTail = getCoordOnRectFromAngle(outerBoxW, outerBoxH, style.tailAngle, Math.min(style.cornerRadius + style.borderWidth, style.cornerRadius + style.borderWidth + style.tailRadius)); outerTail.x = outerTail.x + (tf._width / 2); outerTail.y = outerTail.y + (tf._height / 2); innerBoxW = tf._width + (2 * outerOffset) - (2 * (style.tailRadius + style.borderWidth)); innerBoxH = tf._height + (2 * outerOffset) - (2 * (style.tailRadius + style.borderWidth)); innerTail = getCoordOnRectFromAngle(innerBoxW, innerBoxH, style.tailAngle, Math.max(0, style.cornerRadius - style.tailRadius)); innerTail.x = innerTail.x + (tf._width / 2); innerTail.y = innerTail.y + (tf._height / 2); outerTailPt = getTangentPts(outerTail.x, outerTail.y, innerTail.x, innerTail.y, style.tailRadius + style.borderWidth, style.tailAngle); if( _root.debug ) { border.beginFill(style.borderColor, 50); } else border.beginFill(style.borderColor); drawRect(border, -outerOffset, -outerOffset, tf._width + (2 * outerOffset), tf._height + (2 * outerOffset), style.cornerRadius + style.borderWidth); border.endFill(); if( _root.debug ) { border.beginFill(0x006699); drawOval(border, innerTail.x, innerTail.y, style.tailRadius + style.borderWidth); border.endFill(); } if( _root.debug ) { border.beginFill(0x006699, 20); } else border.beginFill(style.borderColor); border.moveTo(outerTail.x, outerTail.y); border.lineTo(outerTailPt[0].x, outerTailPt[0].y); border.lineTo(outerTailPt[1].x, outerTailPt[1].y); border.endFill(); } static public function getCoordOnRectFromAngle(w, h, angle, cornerRadius) { //return a point on a rectangle using the angle drawn from the center outwards //handle rounded corners by passing to a different function var pt, radians, halfH, halfW; var angleToTR, hwRatio, whRatio; hwRatio = h / w; whRatio = w / h; angleToTR = Math.atan(hwRatio); radians = angle / 180 * Math.PI; halfH = h / 2; halfW = w / 2; pt = new Object(); if( radians >= (2*Math.PI - angleToTR) || (radians <= angleToTR) ) { //right edge if( radians >= (2*Math.PI - angleToTR) ) radians = radians - 2*Math.PI; pt.x = halfW; pt.y = -Math.tan(radians) * halfH * whRatio; if( Math.abs(pt.x) > (halfW - cornerRadius) && (Math.abs(pt.y) > (halfH - cornerRadius)) ) pt = getCoordOnCornerFromXY(halfW, halfH, pt.x, pt.y, cornerRadius, 1); } else if( radians >= angleToTR && (radians <= (Math.PI - angleToTR)) ) { //top edge radians = radians - Math.PI/2; pt.x = Math.tan(-radians) * halfW * hwRatio; pt.y = -halfH; if( Math.abs(pt.x) > (halfW - cornerRadius) && (Math.abs(pt.y) > (halfH - cornerRadius)) ) pt = getCoordOnCornerFromXY(halfW, halfH, pt.x, pt.y, cornerRadius, 2); } else if( radians >= (Math.PI - angleToTR) && (radians <= (Math.PI + angleToTR)) ) { //left edge radians = radians - Math.PI; pt.x = -halfW; pt.y = Math.tan(radians) * halfH * whRatio; if( Math.abs(pt.x) > (halfW - cornerRadius) && (Math.abs(pt.y) > (halfH - cornerRadius)) ) pt = getCoordOnCornerFromXY(halfW, halfH, pt.x, pt.y, cornerRadius, 3); } else if( radians >= (Math.PI + angleToTR) && (radians <= (2*Math.PI - angleToTR)) ) { //bottom edge radians = radians - Math.PI*1.5; pt.x = Math.tan(radians) * halfW * hwRatio; pt.y = halfH; if( Math.abs(pt.x) > (halfW - cornerRadius) && (Math.abs(pt.y) > (halfH - cornerRadius)) ) pt = getCoordOnCornerFromXY(halfW, halfH, pt.x, pt.y, cornerRadius, 4); } return pt; } static public function getCoordOnCornerFromXY(rw, rh, x, y, cornerRadius, side) { // return the (approximate) point on the perimiter of a rounded rectangle when // the intersection is somewhere in the corner portion var pt, signY, signX, percent; if (_root.debug) trace("getCoordOnCornerFromXY"); pt = new Object(); signX = Math.abs(x) / x; if( isNaN(signX) ) { signX = 0; } signY = Math.abs(y) / y; if( isNaN(signY) ) { signY = 0; } if (_root.debug) trace(signX + "::" + signY); switch(side) { case 1 : //right edge (TR or BR corner) percent = (Math.abs(y) - (rh - cornerRadius)) / cornerRadius; if (_root.debug) trace(side + " %" + percent); pt.x = signX * (cornerRadius * Math.cos(percent * Math.PI / 4) + (rw - cornerRadius)); pt.y = signY * (cornerRadius * Math.sin(percent * Math.PI / 4) + (rh - cornerRadius)); return pt; case 2 : //top edge (TL or TR corner) percent = (Math.abs(x) - (rw - cornerRadius)) / cornerRadius; if (_root.debug) trace(side + " %" + percent); pt.x = signX * (cornerRadius * Math.cos(Math.PI/2 - (percent * Math.PI / 4)) + (rw - cornerRadius)); pt.y = signY * (cornerRadius * Math.sin(Math.PI/2 - (percent * Math.PI / 4)) + (rh - cornerRadius)); return pt; case 3 : //left edge (TL or BL corner) percent = (Math.abs(y) - (rh - cornerRadius)) / cornerRadius; if (_root.debug) trace(side + " %" + percent); pt.x = signX * (cornerRadius * Math.cos(percent * Math.PI / 4) + (rw - cornerRadius)); pt.y = signY * (cornerRadius * Math.sin(percent * Math.PI / 4) + (rh - cornerRadius)); return pt; case 4 : //bottom edge (BL or BR corner) percent = (Math.abs(x) - (rw - cornerRadius)) / cornerRadius; if (_root.debug) trace(side + " %" + percent); pt.x = signX * (cornerRadius * Math.cos(Math.PI/2 - (percent * Math.PI / 4)) + (rw - cornerRadius)); pt.y = signY * (cornerRadius * Math.sin(Math.PI/2 - (percent * Math.PI / 4)) + (rh - cornerRadius)); return pt; default : } } static public function getTangentPts(x, y, cx, cy, r, angle) { /* store tangent points on a circle located at (cx, cy) of radius r. pass in the angle from the outside pt to the center pt since it's pre-computed */ var pts = new Array(); var distX = x - cx; var distY = y - cy; var dist = Math.sqrt(distX * distX + (distY * distY)); // use triangulation to find the angles(+/-) to the tangent points var angleToTan = Math.acos(r / dist); // keep everything less than 360 degrees var startAngle = 2*Math.PI - (angle / 180 * Math.PI); var angleA = startAngle - angleToTan; var angleB = startAngle + angleToTan; var sinA = Math.sin(angleA); var cosA = Math.cos(angleA); var sinB = Math.sin(angleB); var cosB = Math.cos(angleB); pts.push({x:cx + (r * cosA), y:cy + (r * sinA)}); pts.push({x:cx + (r * cosB), y:cy + (r * sinB)}); return pts; } // drawOval and drawRect courtesy Ric Ewing // http://www.ricewing.com/ // http://www.macromedia.com/devnet/mx/flash/articles/adv_draw_methods.html static public function drawOval(mc, x, y, radius, yRadius) { var theta, xrCtrl, yrCtrl, angle, angleMid, px, py, cx, cy; if (yRadius == undefined) { yRadius = radius; } theta = Math.PI/4; xrCtrl = radius/Math.cos(theta/2); yrCtrl = yRadius/Math.cos(theta/2); angle = 0; mc.moveTo(x+radius, y); for (var i = 0; i<8; i++) { angle += theta; angleMid = angle-(theta/2); cx = x+Math.cos(angleMid)*xrCtrl; cy = y+Math.sin(angleMid)*yrCtrl; px = x+Math.cos(angle)*radius; py = y+Math.sin(angle)*yRadius; mc.curveTo(cx, cy, px, py); } } static public function drawRect(mc, x, y, w, h, cornerRadius) { if (cornerRadius>0) { // init vars var theta, angle, cx, cy, px, py; // make sure that w + h are larger than 2*cornerRadius if (cornerRadius>Math.min(w, h)/2) { cornerRadius = Math.min(w, h)/2; } // theta = 45 degrees in radians theta = Math.PI/4; // draw top line mc.moveTo(x+cornerRadius, y); mc.lineTo(x+w-cornerRadius, y); //angle is currently 90 degrees angle = -Math.PI/2; // draw tr corner in two parts cx = x+w-cornerRadius+(Math.cos(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); cy = y+cornerRadius+(Math.sin(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); px = x+w-cornerRadius+(Math.cos(angle+theta)*cornerRadius); py = y+cornerRadius+(Math.sin(angle+theta)*cornerRadius); mc.curveTo(cx, cy, px, py); angle += theta; cx = x+w-cornerRadius+(Math.cos(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); cy = y+cornerRadius+(Math.sin(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); px = x+w-cornerRadius+(Math.cos(angle+theta)*cornerRadius); py = y+cornerRadius+(Math.sin(angle+theta)*cornerRadius); mc.curveTo(cx, cy, px, py); // draw right line mc.lineTo(x+w, y+h-cornerRadius); // draw br corner angle += theta; cx = x+w-cornerRadius+(Math.cos(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); cy = y+h-cornerRadius+(Math.sin(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); px = x+w-cornerRadius+(Math.cos(angle+theta)*cornerRadius); py = y+h-cornerRadius+(Math.sin(angle+theta)*cornerRadius); mc.curveTo(cx, cy, px, py); angle += theta; cx = x+w-cornerRadius+(Math.cos(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); cy = y+h-cornerRadius+(Math.sin(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); px = x+w-cornerRadius+(Math.cos(angle+theta)*cornerRadius); py = y+h-cornerRadius+(Math.sin(angle+theta)*cornerRadius); mc.curveTo(cx, cy, px, py); // draw bottom line mc.lineTo(x+cornerRadius, y+h); // draw bl corner angle += theta; cx = x+cornerRadius+(Math.cos(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); cy = y+h-cornerRadius+(Math.sin(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); px = x+cornerRadius+(Math.cos(angle+theta)*cornerRadius); py = y+h-cornerRadius+(Math.sin(angle+theta)*cornerRadius); mc.curveTo(cx, cy, px, py); angle += theta; cx = x+cornerRadius+(Math.cos(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); cy = y+h-cornerRadius+(Math.sin(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); px = x+cornerRadius+(Math.cos(angle+theta)*cornerRadius); py = y+h-cornerRadius+(Math.sin(angle+theta)*cornerRadius); mc.curveTo(cx, cy, px, py); // draw left line mc.lineTo(x, y+cornerRadius); // draw tl corner angle += theta; cx = x+cornerRadius+(Math.cos(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); cy = y+cornerRadius+(Math.sin(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); px = x+cornerRadius+(Math.cos(angle+theta)*cornerRadius); py = y+cornerRadius+(Math.sin(angle+theta)*cornerRadius); mc.curveTo(cx, cy, px, py); angle += theta; cx = x+cornerRadius+(Math.cos(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); cy = y+cornerRadius+(Math.sin(angle+(theta/2))*cornerRadius/Math.cos(theta/2)); px = x+cornerRadius+(Math.cos(angle+theta)*cornerRadius); py = y+cornerRadius+(Math.sin(angle+theta)*cornerRadius); mc.curveTo(cx, cy, px, py); } else { // cornerRadius was not defined or = 0. mc makes it easy. mc.moveTo(x, y); mc.lineTo(x+w, y); mc.lineTo(x+w, y+h); mc.lineTo(x, y+h); mc.lineTo(x, y); } } }