很久前写过一篇Open Flash Chart2初步应用,刚接触的可以看看这篇文章。
最近为了实现动态曲线图展示的功能,对Open Flash Chart2又小小研究了下,研究和整合过程中遇到的各种各样的问题,现在对重点问题的解决方法在此总结下,方便自己日后回顾,也希望能给有同样需求的同仁们提供帮助。
本文的重点就是解决OFC2和第三方JS的冲突,基本目录结构:
[一]、Open Flash Chart2中javascript基本方法的介绍
[二]、OFC2与Prototype.js的冲突解决
[三]、OFC2与Ext.Viewport的冲突解决
[四]、动态流量曲线图的demo
ps:
1.本文demo是在tomcat下发布后测试的,涉及的到部分html页面是不能脱离Web应用服务器的
2.demo中的有关js、swf的路径请根据自己项目的路径具体情况调整
【一】OFC2的javascript基本方法
open_flash_chart_data() 将画图的数据以string的方式传递给chart
ofc_ready() 数据加载好后会执行这个函数的
具体的作用及执行顺序可以试着运行下面这个demo页面:ofc2ChartDemoJs.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Open Flash Chart 2 demo</title> <script type="text/javascript" src="../json2.js"></script> <script type="text/javascript" src="../swfobject.js"></script> </head> <body> <p>Hello World ofc2</p> <div id="my_ofc2_chart"></div> </body> <script type="text/javascript"> var ofc2_chart_id="my_ofc2_chart"; /* swfobject.embedSWF("../open-flash-chart.swf", ofc2_chart_id, "400", "300", "9.0.0", "expressInstall.swf", {"get-data":"getDemoInitData"}, {"wmode":"transparent"} ); */ //如果定义了function:open_flash_chart_data(),则不需要:"get-data":"getDemoInitData"; swfobject.embedSWF("../open-flash-chart.swf", ofc2_chart_id, "400", "300", "9.0.0", "expressInstall.swf", {"wmode":"transparent"} ); function ofc_ready() { alert('ofc_ready'); } function open_flash_chart_data() { alert( 'reading data' ); return JSON.stringify(data_1); } function findSWF(movieName) { if (navigator.appName.indexOf("Microsoft")!= -1) { return window[movieName]; } else { return document[movieName]; } } var data_1 = { "elements": [ { "type": "bar", "values": [9,8,7,6,5,4,3,2,1 ] } ], "title": { "text": "demo 1" } }; function getDemoInitData(){ return JSON.stringify(data_1); } </script> </html> |
运行结果示例图:
OFC2提供function load()方法,可以更新chart的数据,接下来的动态曲线图,也是基于这个方法来实现的,我们先看一个简单的ofc2ChartDemoLoad.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
<%@ page language="java" contentType="text/html; charset=UTF-8"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Open Flash Chart 2 demo</title> <script type="text/javascript" src="../json2.js"></script> <script type="text/javascript" src="../swfobject.js"></script> </head> <body> <p>Hello World ofc2</p> <div id="my_ofc2_chart"></div> <br> <a href="javascript:load_data1('my_ofc2_chart')">display data 1</a> || <a href="javascript:load_data2('my_ofc2_chart')">display data 2</a> </body> <script type="text/javascript"> var ofc2_chart_id="my_ofc2_chart"; swfobject.embedSWF("../open-flash-chart.swf", ofc2_chart_id, "400", "300", "9.0.0", "expressInstall.swf", {"get-data":"getDemoInitData"}, {"wmode":"transparent"} ); function findSWF(movieName) { if (navigator.appName.indexOf("Microsoft")!= -1) { return window[movieName]; } else { return document[movieName]; } } var data_0 = { "elements": [ { "type": "bar", "values": [1,2,3,4,5,6,7,8,9] } ], "title": { "text": "demo" } }; var data_1 = { "elements": [ { "type": "bar", "values": [9,8,7,6,5,4,3,2,1] } ], "title": { "text": "demo data 1" } }; var data_2={ "title":{ "text":"每周水果产量" }, "elements":[ { "type":"bar_glass", "values":[16,8,18,12,16,24,6] } ], "x_axis":{ "labels":{ "labels":["星期1","星期2","星期3","星期4","星期5","星期6","星期日"] } }, "y_axis":{ "max":30 } }; function getDemoInitData(){ return JSON.stringify(data_0); } function load_data1(movieName) { var tmp = findSWF(movieName); tmp.load(JSON.stringify(data_1)); } function load_data2(movieName) { var tmp = findSWF(movieName); tmp.load(JSON.stringify(data_2)); } </script></html> |
运行结果示例图:
【二】、OFC2与prototype.js冲突问题
在我实验的过程中发现,如果只是第一次加载flash,运用函数swfobject.embedSWF(),设置参数”data-file”加载后台json数据时,flash能正常画出曲线图和prototype、jquery没有冲突,IE、FF都能正常显示。但是如果需要调用上面介绍的 load(JSON.stringify(data))这个方法重新画图时会失败,具体现象:在IE下chart不会更新数据,在FF下chart会消失。最后发现JSON.stringify(data) 和prototype.js中的JSON转化为string的方法冲突引起的(prototype1.6.0.3和1.6.1都有冲突),最开始是怀疑json2.js和prototype.js的JSON.stringify(data)转化后的格式不一致引起的,在接下来的测试和调试中发现转化后的格式的确不一致,但这个发现不是问题的根源,最后找了一个折中的方法解决了这个问题:
- 先把json2.js这个文件从页面中里移除
- 然后load(JSON.stringify(chartdata))改成load(Object.toJSON(chartdata))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
<%@ page language="java" contentType="text/html; charset=UTF-8"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Open Flash Chart 2 demo</title> <script type="text/javascript" src="../../scripts/prototype-1.6.0.2.js"></script> <script type="text/javascript" src="../swfobject.js"></script> </head> <body> <p>Hello World ofc2</p> <div id="my_ofc2_chart"></div> <br> <input type="button" name="btn" value="stop" onclick="changeRefresh(this)"> </body> <script type="text/javascript"> var portType = "random"; var portId = "1000"; var portIndex = "5"; var ofc2_chart_id="my_ofc2_chart"; var ofc2_url = 'DrawOfc2RealTimeAction.do'; var flash_param="ofc2_param="+ofc2_chart_id+","+portType+","+portId+","+portIndex; swfobject.embedSWF("../open-flash-chart.swf", ofc2_chart_id, "600", "300", "9.0.0", "expressInstall.swf", {"data-file":ofc2_url+"?"+flash_param}, {"wmode":"transparent"} ); //refresh_ofc2_chart(); var o_time = setInterval('refresh_ofc2_chart()', 10*1000); function changeRefresh(obj){ if(obj.value=="stop"){ if(null!=o_time){clearInterval(o_time );} obj.value="start" }else{ clearInterval(o_time ); o_time = setInterval('refresh_ofc2_chart()', 10*1000); obj.value="stop" } } function refresh_ofc2_chart(){ //var pars = Object.toQueryString(par); var myAjax = new Ajax.Request(ofc2_url, { encoding :'UTF-8', method :'get', parameters :flash_param, asynchronous :false, onComplete : showResponse } ); } function showResponse(rep) { var chartdata = rep.responseText.evalJSON(true); //alert(JSON.stringify(chartdata)); var tmp = findSWF(ofc2_chart_id); x = tmp.load(Object.toJSON(chartdata)); } function findSWF(movieName) { if (navigator.appName.indexOf("Microsoft")!= -1) { return window[movieName]; } else { return document[movieName]; } } </script></html> |
【三】OFC2与Ext.Viewport的冲突问题
如果你的页面里用到了Ext.Viewport来布局,会发现在IE下findSWF(‘ofc2_id’).load(Object.toJSON(chartdata));再一次的发生了杯具,FF下一切正常,而在IE下页面会有js错误提示:“未指明的错误”,这个错误的原因至今没有找到,js debug以及后来查了很多资料也没有确切结果,最后在ext的论坛上找了一个有关该问题的讨论,提供了一个扩展js库uxmedia,可以解决该问题,
相关讨论的帖子:http://www.sencha.com/forum/showthread.php?23983-ux.Media-.Flash-Flex-Components-2.1.2
uxmedia的官网:http://code.google.com/p/uxmedia/
uxmedia API doc:http://uxdocs.theactivegroup.com/
基本的解决方法:
- 导入uxmedia的相关js文件要注意文件的先后顺序
- 用Ext.ux.Chart.OFC.Component创建flash chart
- 修改更新数据的相关方法:把findSWF(ofc2_chart_id).load(JSON.stringify(chartdata))改成:
- Ext.getDom(ofc2_chart_id).load(Object.toJSON(chartdata));//prototype.js
- Ext.getDom(ofc2_chart_id).load(JSON.stringify(chartdata));//json2.js
页面代码片段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
<%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ include file="/common/taglibs.jsp"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Open Flash Chart 2 demo</title> <!-- 其他有关Extjs的相关文件自己导入,这里省略--> <script type="text/javascript" src="ofc2/swfobject.js"></script> <!-- 注意下面uxmedia的相关文件的顺序--> <script type="text/javascript" src="scripts/ext/ux.media/uxvismode.js"></script> <script type="text/javascript" src="scripts/ext/ux.media/uxmedia.js"></script> <script type="text/javascript" src="scripts/ext/ux.media/uxmedia-ie.js"></script> <script type="text/javascript" src="scripts/ext/ux.media/uxflash.js"></script> <script type="text/javascript" src="scripts/ext/ux.media/uxchart.js"></script> <script type="text/javascript" src="scripts/ext/ux.media/uxofc.js"></script> </head> <body> <p>Hello World ofc2</p> <div id="div_panel_t"></div> <div id="div_panel_w"></div> <div id="div_panel_c"> <div align="center" id="div_iframe"> <div id="div_my_ofc2_chart"></div> <div id="my_ofc2_chart"></div> <br> <div style="text-align: center; font-size: 9pt"><input type="button" name="btn_control" onclick="changeBtnStatus(this)" value="暂停"></div> </div> </div> </body> <script type="text/javascript"> var portType = "random"; var portId = "1000"; var portIndex = "5"; var refresh_Interval=10; var ofc2_chart_id="my_ofc2_chart"; var ofc2_url = '/DrawOfc2RealTimeAction.do'; var flash_param="ofc2_param="+ofc2_chart_id+","+portType+","+portId+","+portIndex; var obj_refresh = setInterval('refresh_ofc2_chart()', refresh_Interval*1000); function findSWF(movieName) { if (navigator.appName.indexOf("Microsoft")!= -1) { return window[movieName]; } else { return document[movieName]; } } function refresh_ofc2_chart(){ var myAjax = new Ajax.Request(ofc2_url, { encoding :'UTF-8', method :'get', parameters :flash_param, asynchronous :false, onComplete : showResponse }); } function showResponse(rep) { var chartdata = rep.responseText.evalJSON(true); //var tmp = findSWF(ofc2_chart_id); //tmp.load(JSON.stringify(chartdata)) var tmp = Ext.getDom(ofc2_chart_id); tmp.load(Object.toJSON(chartdata)); } function changeBtnStatus(o_btn){ if(null!=obj_refresh){clearInterval(obj_refresh );} if("继续"==o_btn.value){ obj_refresh = setInterval('refresh_ofc2_chart()', refresh_Interval*1000); o_btn.value="暂停"; }else{ o_btn.value="继续"; } } function createExtOFC2Comp(_ofc2_id){ var _ux_OFC_Comp = new Ext.ux.Chart.OFC.Component({ height : 300, width : 500, renderTo: 'div_'+_ofc2_id, chartCfg:{ id : _ofc2_id, autoSize : true, renderOnResize : false, disableCaching : true } //,mediaMask : {msg:'Loading OpenChart Object'} ,loadMask : {msg:'Gathering Chart Data'} ,autoMask : true ,id :'demoOFC' ,chartURL : 'ofc2/open-flash-chart.swf' /* Chart data loading options */ //,chartData : data //initial load - pre-defined JSON series ,dataURL : ofc2_url+'?'+flash_param // SWF-Managed remote request in JSON format ,previews : new Array() }); return _ux_OFC_Comp; } Ext.onReady(function() { var west_panel = new Ext.Panel( { region : 'west', id: "sidebar", split : true, width : 180, minSize : 100, maxSize : 200, title : " ", items : [], autoScroll : true, collapsible : true, frame : true }); var _ofc2_comp = createExtOFC2Comp(ofc2_chart_id); //_ofc2_comp.show(); var viewport = new Ext.Viewport( { id : 'hxlayout', layout : 'border', layoutConfig : { animate : true }, defaults : { border : false, split : false }, items : [ { region : 'north', id : 'cmp_panel_t', contentEl : 'div_panel_t', height : 40 },west_panel,{ region : 'center', id : 'top_cmp_panel_c', contentEl : 'div_panel_c', autoScroll : true, items:[_ofc2_comp] } ] }); }); </script> </html> |
效果图如下:
【四】动态曲线的简单demo代码
实时流量动态图的效果如上,基本实现的代码如下,demo是个随机数模拟流量的(有关snmp采集端口流量的代码就不在这里贴出了 ):
FlowChart2Vo .java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
/** * @author michael * */ public class FlowChart2Vo implements Serializable { /** * serialVersionUID */ private static final long serialVersionUID = -8305542118777147896L; private String dataKey; private String chartId; private double outLastValue; private double inLastValue; private long outLastTime; private long inLastTime; private int maxDataCount = 10; private Double ymax = 1d; private CommunityTarget target = null; private String portIndex; private List<String> xlabelList = new ArrayList<String>(); private List<Double> inFlowList = new ArrayList<Double>(); private List<Double> outFlowList = new ArrayList<Double>(); /** * @param inFlow * @param outFlow * @param xlabel */ public void addFlowData(Double inFlow, Double outFlow, String xlabel) { if (this.inFlowList.size() > this.maxDataCount) { this.inFlowList.remove(0); this.outFlowList.remove(0); this.xlabelList.remove(0); } this.inFlowList.add(inFlow); this.outFlowList.add(outFlow); this.xlabelList.add(xlabel); } /** * @return */ public Double getMaxOutFlow() { List<Double> temp = new ArrayList<Double>(); for (Double dou : outFlowList) { if (null != dou && !Double.isNaN(dou)) { temp.add(dou); } } Collections.sort(temp); if (temp.size() < 1) { return 0d; } return temp.get(temp.size() - 1); } /** * @return */ public Double getMaxInFlow() { List<Double> temp = new ArrayList<Double>(); for (Double dou : inFlowList) { if (null != dou && !Double.isNaN(dou)) { temp.add(dou); } } Collections.sort(temp); if (temp.size() < 1) { return 0d; } return temp.get(temp.size() - 1); } //get set方法省略 //......... |
DrawOfc2RealTimeDemoAction.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
package michael.ofc2.action; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import jofc2.model.Chart; import jofc2.model.Text; import jofc2.model.axis.XAxis; import jofc2.model.axis.YAxis; import jofc2.model.elements.LineChart; import jofc2.model.elements.LineChart.Dot; import michael.ofc2.vo.FlowChart2Vo; import org.apache.commons.lang.StringUtils; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.snmp4j.CommunityTarget; /** * Open flash chart 2 * @author Michael sun */ public class DrawOfc2RealTimeDemoAction extends Action { /** * draw chart * @param mapping struts mapping * @param form struts form * @param request http request * @param response http response * @return action forward * @throws Exception any execption */ @SuppressWarnings("unchecked") public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { response.setContentType("application/json"); response.setHeader("Cache-Control", "no-cache"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); String ofc2_param = request.getParameter("ofc2_param"); String[] params = ofc2_param.split(","); String chartId = params[0];// request.getParameter("ofcChartId"); String flashType = params[1]; String equId = params[2];// request.getParameter("equId"); String portIndex = params[3];// request.getParameter("portIndex"); String maxDataCount = null; if (params.length > 4) { maxDataCount = params[4];// request.getParameter("maxDataCount"); } String dataKey = equId + "-" + portIndex; Map<String, FlowChart2Vo> rtdataMap = (Map<String, FlowChart2Vo>) request .getSession().getAttribute("SE_FLOW_RTDATA_MAP"); if (null == rtdataMap) { rtdataMap = new HashMap<String, FlowChart2Vo>(); } FlowChart2Vo rtDatavo = rtdataMap.get(dataKey); if (null == rtDatavo) { rtDatavo = new FlowChart2Vo(); rtDatavo.setDataKey(dataKey); rtDatavo.setChartId(chartId); rtDatavo.setPortIndex(portIndex); CommunityTarget target = null; rtDatavo.setTarget(target); } if (null != maxDataCount && !"".equals(maxDataCount)) { rtDatavo.setMaxDataCount(Integer.parseInt(maxDataCount)); } if ("random".equals(flashType)) { this.getRandomData(rtDatavo); } else if (StringUtils.isNotEmpty(rtDatavo.getPortIndex())) { this.getSnmpFlowData(rtDatavo); } rtdataMap.put(dataKey, rtDatavo); request.getSession().setAttribute("SE_FLOW_RTDATA_MAP", rtdataMap); String chartString = this.createLineChart(rtDatavo); System.out.println(chartString); out.write(chartString); out.close(); return null; } /** * 随机数 * @param rtDatavo */ private void getRandomData(FlowChart2Vo rtDatavo) { String xlabel = getFormatDate(new Date(), "mm:ss"); double inSpeed = 2 + Math.random() * 3; double outSpeed = 1 + Math.random() * 3; rtDatavo.addFlowData(inSpeed, outSpeed, xlabel); } /** * snmp 采集端口的出入速率 * @param rtDatavo */ private void getSnmpFlowData(FlowChart2Vo rtDatavo) { // ...... } /** * 生成ofc2的json格式数据 * @param rtDatavo * @return */ private String createLineChart(FlowChart2Vo rtDatavo) { Chart ofc2Chart = new Chart(); Text title = new Text("demo"); ofc2Chart.setTitle(title); ofc2Chart.setXLegend(new Text("time", "{color: #736AFF;}")); ofc2Chart.setYLegend(new Text("Flow(Mb/s)", "{color: #736AFF;}")); LineChart lineChartIn = new LineChart(); lineChartIn.setColour("#00FF00"); lineChartIn.setText("in"); LineChart lineChartOut = new LineChart(); lineChartOut.setColour("#0000FF"); lineChartOut.setText("out"); List<Double> inFlowList = rtDatavo.getInFlowList(); List<Double> outFlowList = rtDatavo.getOutFlowList(); List<String> xlabelList = rtDatavo.getXlabelList(); if (inFlowList.size() > 0 && outFlowList.size() > 0) { double tmpymax = Math.max(rtDatavo.getMaxInFlow(), rtDatavo .getMaxOutFlow()); if (tmpymax > rtDatavo.getYmax()) { rtDatavo.setYmax(tmpymax); } } double ymax = rtDatavo.getYmax(); YAxis y = new YAxis(); // y 轴 y.setMax(ymax); // y 轴最大值 y.setSteps(ymax / 10); // y 轴步进 XAxis xaxis = new XAxis(); // X 轴 for (int i = 0; i < inFlowList.size(); i++) { xaxis.addLabels(xlabelList.get(i)); Dot indot = new Dot(inFlowList.get(i)); indot.setDotSize(2); indot.setHaloSize(2); lineChartIn.addDots(indot); Dot dot2 = new Dot(outFlowList.get(i)); dot2.setDotSize(2); dot2.setHaloSize(2); lineChartOut.addDots(dot2); } ofc2Chart.addElements(lineChartIn); ofc2Chart.addElements(lineChartOut); ofc2Chart.setXAxis(xaxis); ofc2Chart.setYAxis(y); return ofc2Chart.toString(); } /** * 将日期(Date形式)按格式转化成日期(String形式). * @param date 日期(Date形式) * @param formatStr 日期格式 * @return dateStr 日期(String形式) */ public static String getFormatDate(Date date, String formatStr) { String dateStr = ""; SimpleDateFormat sdf = new SimpleDateFormat(formatStr, Locale.UK); dateStr = sdf.format(date); return dateStr; } } |
原创文章,转载请注明: 转载自micmiu – 软件开发+生活点滴[ http://www.micmiu.com/ ]
你好,我想实现线性曲线图的效果。你上面给的例子中没有给全部的引入。我对ext不了解,请问怎么实现。能给我介绍下需要引入哪些完整的东西嘛?
你如果不是用ext框架的,不需要这么复杂(我这个blog主要是解决它和ext冲突的),
具体实现只要到官网看看demo即可了
你好,我现在在做snmp实时流量的采集,看了你这篇文章“动态曲线的简单demo代码”部分,感觉很我做的功能非常相似。想请教一个问题,x轴的时间点能否都显示成整点?比如03:10,03:20,03:30类似这样?
可以的 这个你可以自己控制的