JavaScript绘图类 (DIV绘图)
主要的图形算法抄自一个叫w_jsGraphics.js的类库,第一次看到那个库的时候,感觉那是十分神奇的存在。
不过估计现在那个库早就已经找不到了.
这是很早之前的一个DIV绘图类,那时候VML+SVG的JS图库还没流行。
最初的神创造一个点,然后由这个点便画出一条线,最后这条线绘出了一个个世界。
/** *JS 绘图类 Graphics- 0.02 *@author <a href="mailto: redrainyi@gmail.com">yao</a> *@param canvas 画布(画布可以是 DIV(IE) 或 Layer(Netscape) *参考资料《w_jsGraphics.js》《DHTML手册》《BISOFTJS》《Computer Graphics》 *CopyLeft 2007 在引用或转载时请保持文件代码的完整性 */ function Graphics(canvas) { /*获得画板以后的绘图都是在画板上实现*/ this.canvas = typeof(canvas)=="string"?document.getElementById(canvas):canvas; /*默认颜色 #000000黑色*/ this.color = '#000000'; /*设置线默认象素为 1px*/ this.setStroke(1); /*文本默认字体*/ this.setFont('verdana,geneva,helvetica,sans-serif', 20, "PLAIN"); /*创建一个Div元素作为根结点 所有绘图的根结点*/ this.documentRoot = document.createElement("div"); } Graphics.prototype = { canvas : window, color : '#000000', stroke : 1, documentRoot : null, /*设置颜色 格式"#RRGGBB" 颜色会保留到下一次再重新设置为止*/ setColor : function(c) { this.color = c; }, /*绘制点DIV*/ drawDiv : function(x, y, w, h) { var elem = document.createElement("div");/*创建一个Div元素*/ elem.style.position = 'absolute'; elem.style.overflow = 'hidden'; elem.style.left = x; elem.style.top = y; elem.style.width = w; elem.style.height = h ; elem.style.backgroundColor = this.color; this.documentRoot.appendChild(elem); }, /*绘制组件 (绘图)*/ paint : function() { this.canvas.appendChild(this.documentRoot);/*将根结点添加到画布上*/ }, /*清空画板*/ clear : function() { if (this.canvas) this.canvas.innerHTML = ''; }, /*设置画笔 参数代表点(象素)大小 默认是 1px (设置为-1时候代表 虚点线)*/ setStroke : function(x) { this.stroke = x; if (!(x+1)) /*javaScript中 0为假 */ { /*设置画法为虚点*/ this.drawLine = this.drawLineDott; this.drawOval = this.drawOvalDott; this.drawRect = this.drawRectangleDott; } else if (x > 1) { /*实线*/ this.drawLine = this.drawLine2D; this.drawOval = this.drawOval2D; this.drawRect = this.drawRectangle; } else { /*/象素大小(厚度)为1px的线*/ this.drawLine = this.drawLine1D; this.drawOval = this.drawOval1D; this.drawRect = this.drawRectangle; } }, /*画虚线*/ drawLineDott : function(x1, y1, x2, y2) { if (x1 > x2) { var _x2 = x2; var _y2 = y2; x2 = x1; y2 = y1; x1 = _x2; y1 = _y2; } var dx = x2-x1, dy = Math.abs(y2-y1), x = x1, y = y1, yIncr = (y1 > y2)? -1 : 1, drw = true; if (dx >= dy) { var pr = dy<<1, pru = pr - (dx<<1), p = pr-dx; while ((dx--) > 0) { if (drw) this.drawDiv(x, y, 1, 1); drw = !drw; if (p > 0) { y += yIncr; p += pru; } else p += pr; ++x; } if (drw) this.drawDiv(x, y, 1, 1); } else { var pr = dx<<1, pru = pr - (dy<<1), p = pr-dy; while ((dy--) > 0) { if (drw) this.drawDiv(x, y, 1, 1); drw = !drw; y += yIncr; if (p > 0) { ++x; p += pru; } else p += pr; } if (drw) this.drawDiv(x, y, 1, 1); } }, /*画线(经过优化的绘线方法)*/ drawLine2D : function(x1, y1, x2, y2) { if (x1 > x2) { var _x2 = x2; var _y2 = y2; x2 = x1; y2 = y1; x1 = _x2; y1 = _y2; } var dx = x2-x1, dy = Math.abs(y2-y1), x = x1, y = y1, yIncr = (y1 > y2)? -1 : 1; var s = this.stroke; if (dx >= dy) { if (dx > 0 && s-3 > 0) { var _s = (s*dx*Math.sqrt(1+dy*dy/(dx*dx))-dx-(s>>1)*dy) / dx; _s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1; } else var _s = s; var ad = Math.ceil(s/2); var pr = dy<<1, pru = pr - (dx<<1), p = pr-dx, ox = x; while ((dx--) > 0) { ++x; if (p > 0) { this.drawDiv(ox, y, x-ox+ad, _s); y += yIncr; p += pru; ox = x; } else p += pr; } this.drawDiv(ox, y, x2-ox+ad+1, _s); }else{ if (s-3 > 0) { var _s = (s*dy*Math.sqrt(1+dx*dx/(dy*dy))-(s>>1)*dx-dy) / dy; _s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1; } else var _s = s; var ad = Math.round(s/2); var pr = dx<<1, pru = pr - (dy<<1), p = pr-dy, oy = y; if (y2 <= y1) { ++ad; while ((dy--) > 0) { if (p > 0) { this.drawDiv(x++, y, _s, oy-y+ad); y += yIncr; p += pru; oy = y; } else { y += yIncr; p += pr; } } this.drawDiv(x2, y2, _s, oy-y2+ad); } else { while ((dy--) > 0) { y += yIncr; if (p > 0) { this.drawDiv(x++, oy, _s, y-oy+ad); p += pru; oy = y; } else p += pr; } this.drawDiv(x2, oy, _s, y2-oy+ad+1); } } }, /*画线(象素大小为1的线 参考Bresenham算法)*/ drawLine1D : function(x1, y1, x2, y2) { if (x1 > x2) { var _x2 = x2; var _y2 = y2; x2 = x1; y2 = y1; x1 = _x2; y1 = _y2; } var dx = x2-x1, dy = Math.abs(y2-y1), x = x1, y = y1, yIncr = (y1 > y2)? -1 : 1; if (dx >= dy) { var pr = dy<<1, pru = pr - (dx<<1), p = pr-dx, ox = x; while ((dx--) > 0) { ++x; if (p > 0) { this.drawDiv(ox, y, x-ox, 1); y += yIncr; p += pru; ox = x; } else p += pr; } this.drawDiv(ox, y, x2-ox+1, 1); } else { var pr = dx<<1, pru = pr - (dy<<1), p = pr-dy, oy = y; if (y2 <= y1) { while ((dy--) > 0) { if (p > 0) { this.drawDiv(x++, y, 1, oy-y+1); y += yIncr; p += pru; oy = y; } else { y += yIncr; p += pr; } } this.drawDiv(x2, y2, 1, oy-y2+1); } else { while ((dy--) > 0) { y += yIncr; if (p > 0) { this.drawDiv(x++, oy, 1, y-oy); p += pru; oy = y; } else p += pr; } this.drawDiv(x2, oy, 1, y2-oy+1); } } }, /*圆弧 八个对称点的算法*/ drawCirclePoints : function(cx, cy, xl, xr, yt, yb, w, h) { this.drawDiv(xr+cx, yt+cy, w, h); this.drawDiv(xr+cx, yb+cy, w, h); this.drawDiv(xl+cx, yb+cy, w, h); this.drawDiv(xl+cx, yt+cy, w, h); }, /*圆形(椭圆形 卵形)*/ drawOval2D : function(left, top, width, height) { var s = this.stroke; width += s-1; height += s-1; var a = width>>1, b = height>>1, wod = width&1, hod = (height&1)+1, cx = left+a, cy = top+b, x = 0, y = b, aa = (a*a)<<1, bb = (b*b)<<1, st = (aa>>1)*(1-(b<<1)) + bb, tt = (bb>>1) - aa*((b<<1)-1); if (s-4 < 0 && (!(s-2) || width-51 > 0 && height-51 > 0)) { var ox = 0, oy = b, w, h, pxl, pxr, pxt, pxb, pxw; while (y > 0) { if (st < 0) { st += bb*((x<<1)+3); tt += (bb<<1)*(++x); } else if (tt < 0) { st += bb*((x<<1)+3) - (aa<<1)*(y-1); tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3); w = x-ox; h = oy-y; if (w-1) { pxw = w+1+(s&1); h = s; } else if (h-1) { pxw = s; h += 1+(s&1); } else pxw = h = s; this.drawCirclePoints(cx, cy, -x+1, ox-pxw+w+wod, -oy, -h+oy+hod, pxw, h); ox = x; oy = y; } else { tt -= aa*((y<<1)-3); st -= (aa<<1)*(--y); } } this.drawDiv(cx-a, cy-oy, s, (oy<<1)+hod); this.drawDiv(cx+a+wod-s+1, cy-oy, s, (oy<<1)+hod); } else { var _a = (width-((s-1)<<1))>>1, _b = (height-((s-1)<<1))>>1, _x = 0, _y = _b, _aa = (_a*_a)<<1, _bb = (_b*_b)<<1, _st = (_aa>>1)*(1-(_b<<1)) + _bb, _tt = (_bb>>1) - _aa*((_b<<1)-1), pxl = new Array(), pxt = new Array(), _pxb = new Array(); pxl[0] = 0; pxt[0] = b; _pxb[0] = _b-1; while (y > 0) { if (st < 0) { st += bb*((x<<1)+3); tt += (bb<<1)*(++x); pxl[pxl.length] = x; pxt[pxt.length] = y; } else if (tt < 0) { st += bb*((x<<1)+3) - (aa<<1)*(y-1); tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3); pxl[pxl.length] = x; pxt[pxt.length] = y; } else { tt -= aa*((y<<1)-3); st -= (aa<<1)*(--y); } if (_y > 0) { if (_st < 0) { _st += _bb*((_x<<1)+3); _tt += (_bb<<1)*(++_x); _pxb[_pxb.length] = _y-1; } else if (_tt < 0) { _st += _bb*((_x<<1)+3) - (_aa<<1)*(_y-1); _tt += (_bb<<1)*(++_x) - _aa*(((_y--)<<1)-3); _pxb[_pxb.length] = _y-1; } else { _tt -= _aa*((_y<<1)-3); _st -= (_aa<<1)*(--_y); _pxb[_pxb.length-1]--; } } } var ox = 0, oy = b, _oy = _pxb[0], l = pxl.length, w, h; for (var i = 0; i < l; i++) { if (typeof _pxb[i] != "undefined") { if (_pxb[i] < _oy || pxt[i] < oy) { x = pxl[i]; this.drawCirclePoints(cx, cy, -x+1, ox+wod, -oy, _oy+hod, x-ox, oy-_oy); ox = x; oy = pxt[i]; _oy = _pxb[i]; } } else { x = pxl[i]; this.drawDiv(cx-x+1, cy-oy, 1, (oy<<1)+hod); this.drawDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod); ox = x; oy = pxt[i]; } } this.drawDiv(cx-a, cy-oy, 1, (oy<<1)+hod); this.drawDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod); } }, /*线象素为1 的圆形*/ drawOval1D : function(x1, y1, w1, h1) { var a = w1>>1, b = h1>>1, wod = w1&1, hod = (h1&1)+1, cx = x1+a, cy = y1+b, x = 0, y = b, ox = 0, oy = b, aa = (a*a)<<1, bb = (b*b)<<1, st = (aa>>1)*(1-(b<<1)) + bb, tt = (bb>>1) - aa*((b<<1)-1), w, h; while (y > 0) { if (st < 0) { st += bb*((x<<1)+3); tt += (bb<<1)*(++x); } else if (tt < 0) { st += bb*((x<<1)+3) - (aa<<1)*(y-1); tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3); w = x-ox; h = oy-y; if (w&2 && h&2) { this.drawCirclePoints(cx, cy, -x+2, ox+wod, -oy, oy-1+hod, 1, 1); this.drawCirclePoints(cx, cy, -x+1, x-1+wod, -y-1, y+hod, 1, 1); } else this.drawCirclePoints(cx, cy, -x+1, ox+wod, -oy, oy-h+hod, w, h); ox = x; oy = y; } else { tt -= aa*((y<<1)-3); st -= (aa<<1)*(--y); } } this.drawDiv(cx-a, cy-oy, a-ox+1, (oy<<1)+hod); this.drawDiv(cx+ox+wod, cy-oy, a-ox+1, (oy<<1)+hod); }, /*虚点圆形*/ drawOvalDott : function(x1, y1, w1, h1) { var a = w1>>1, b = h1>>1, wod = w1&1, hod = h1&1, cx = x1+a, cy = y1+b, x = 0, y = b, aa2 = (a*a)<<1, aa4 = aa2<<1, bb = (b*b)<<1, st = (aa2>>1)*(1-(b<<1)) + bb, tt = (bb>>1) - aa2*((b<<1)-1), drw = true; while (y > 0) { if (st < 0) { st += bb*((x<<1)+3); tt += (bb<<1)*(++x); } else if (tt < 0) { st += bb*((x<<1)+3) - aa4*(y-1); tt += (bb<<1)*(++x) - aa2*(((y--)<<1)-3); } else { tt -= aa2*((y<<1)-3); st -= aa4*(--y); } if (drw) this.drawCirclePoints(cx, cy, -x, x+wod, -y, y+hod, 1, 1); drw = !drw; } }, /*绘制矩形(边为虚线的矩形)*/ drawRectangleDott : function(x, y, w, h) { this.drawLine(x, y, x+w, y); this.drawLine(x+w, y, x+w, y+h); this.drawLine(x, y+h, x+w, y+h); this.drawLine(x, y, x, y+h); }, /*绘制矩形(边为实线的矩形)*/ drawRectangle : function(x, y, w, h) { var s = this.stroke; this.drawDiv(x, y, w, s); this.drawDiv(x+w, y, s, h); this.drawDiv(x, y+h, w+s, s); this.drawDiv(x, y+s, s, h-s); }, /*绘制折线 参数是每个点xy轴坐标数祖集合*/ drawPolyLine : function(xPoints, yPoints) { for (var i=0 ; i<xPoints.length-1 ; i++ ) { this.drawLine(xPoints[i], yPoints[i], xPoints[i+1], yPoints[i+1]); }; }, /*填充图形部分*/ /*填充矩形*/ fillRect : function(x, y, w, h) { this.drawDiv(x, y, w, h); }, /*多边形 与折线类似不同的地方是多边形是闭合的*/ drawPolygon : function(xPoints, yPoints) { this.drawPolyline(xPoints, yPoints); this.drawLine(xPoints[x.length-1], yPoints[x.length-1], x[0], y[0]); }, /*填充的圆形(椭圆形)*/ fillOval : function(left, top, w, h) { var a = (w -= 1)>>1, b = (h -= 1)>>1, wod = (w&1)+1, hod = (h&1)+1, cx = left+a, cy = top+b, x = 0, y = b, ox = 0, oy = b, aa2 = (a*a)<<1, aa4 = aa2<<1, bb = (b*b)<<1, st = (aa2>>1)*(1-(b<<1)) + bb, tt = (bb>>1) - aa2*((b<<1)-1), pxl, dw, dh; if (w+1) while (y > 0) { if (st < 0) { st += bb*((x<<1)+3); tt += (bb<<1)*(++x); } else if (tt < 0) { st += bb*((x<<1)+3) - aa4*(y-1); pxl = cx-x; dw = (x<<1)+wod; tt += (bb<<1)*(++x) - aa2*(((y--)<<1)-3); dh = oy-y; this.drawDiv(pxl, cy-oy, dw, dh); this.drawDiv(pxl, cy+y+hod, dw, dh); ox = x; oy = y; } else { tt -= aa2*((y<<1)-3); st -= aa4*(--y); } } this.drawDiv(cx-a, cy-oy, w+1, (oy<<1)+hod); }, /** *填充的多边形 参数 x坐标数组 y坐标数组 *数组示例: *var Xpoints = new Array(10,80,80,50); *var YPoints = new Array(50,10,90,80); *jg.drawPolyline(Xpoints,Ypoints); */ fillPolygon : function(array_x, array_y) { var i; var y; var miny, maxy; var x1, y1; var x2, y2; var ind1, ind2; var ints; var n = array_x.length; /*数组长度 正常输入的情况 array_x.length == array_y.length;*/ if (!n) return; /*如果输入数组为空 直接返回*/ /*得到最大于最小的y坐标*/ miny = array_y[0]; maxy = array_y[0]; for (i = 1; i < n; i++) { if (array_y[i] < miny) miny = array_y[i]; if (array_y[i] > maxy) maxy = array_y[i]; } /*最小Y坐标 -> 最大Y坐标*/ for (y = miny; y <= maxy; y++) { var polyInts = new Array(); ints = 0; /*参数数组 -> */ for (i = 0; i < n; i++) { if (!i) // if( i != 0) { ind1 = n-1; ind2 = 0; } else { ind1 = i-1; ind2 = i; } y1 = array_y[ind1]; y2 = array_y[ind2]; if (y1 < y2) { x1 = array_x[ind1]; x2 = array_x[ind2]; } else if (y1 > y2) { y2 = array_y[ind1]; y1 = array_y[ind2]; x2 = array_x[ind1]; x1 = array_x[ind2]; } else continue; if ((y >= y1) && (y < y2)) polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1); else if ((y == maxy) && (y > y1) && (y <= y2)) polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1); } /*sort函数:默认根据ASCII升序排列,这里使用用函数(integer_compare)来确定元素顺序*/ polyInts.sort(new Function('x,y', 'return (x < y) ? -1 : ((x > y)*1);')); for (i = 0; i < ints; i+=2) this.drawDiv(polyInts[i], y, polyInts[i+1]-polyInts[i]+1, 1); } }, /*文本 参数为x y坐标 文本字符串*/ drawString : function(txt, x, y) { var elem = document.createElement("div"); elem.style.position = 'absolute'; elem.style.overflow = 'hidden'; elem.style.left = x ; elem.style.top = y ; /*elem.style.backgroundColor = this.color;*/ elem.style.fontFamily = this.fFamily; elem.style.fontSize = this.fSize; elem.style.color = this.color; /*字体属性*/ if(this.fStyle=="PLAIN"){ elem.style.fontweight = 'normal';/*默认字体*/ }else if(this.fStyle=="ITALIC"){ elem.style.fontweight = 'bold';/*斜体字*/ }else if(this.fStyle=="BOLD"){ elem.style.fontStyle = 'italic';/*斜体字*/ }else if(this.fStyle=="ITALIC_BOLD"){ elem.style.fontweight = 'bold';/*粗体字*/ elem.style.fontStyle = 'italic';/*斜体字*/ }else if((this.fStyle=="BOLD_ITALIC")){ elem.style.fontStyle = 'italic';/*斜体字*/ elem.style.fontweight = 'bold';/*粗体字*/ }else{ elem.style.fontweight = 'normal';/*默认字体PLAIN*/ } elem.appendChild(document.createTextNode(txt)); this.documentRoot.appendChild(elem); }, /**设置字体 * 参数:(字形 尺寸 样式) * 尺寸:(单位象素) * 样式:(PLAIN 默认,BOLD 粗体,ITALIC 斜体,ITALIC_BOLD 斜粗体,BOLD_ITALIC 粗斜体) */ setFont : function(fFamily, fSize, fStyle) { this.fFamily = fFamily; this.fSize = fSize; this.fStyle = fStyle; }, /*添加图片 图片地址 位置宽高*/ drawImage : function(imgSrc, x, y, w, h) { var elem = document.createElement("div"); elem.style.position = 'absolute'; elem.style.overflow = 'hidden'; elem.style.left = x ; elem.style.top = y ; elem.style.width = w ; elem.style.height= h ; var elemImg = document.createElement("img"); elemImg.src = imgSrc; elemImg.style.left = x ; elemImg.style.top = y ; elemImg.style.width = w ; elemImg.style.height= h ; elem.appendChild(elemImg); this.documentRoot.appendChild(elem); } };
甚以此纪念充满激情的岁月。