背景
在專案中使用highcharts是很容易的,移動端也適配的不錯,按照官網教程即可。但是在移動端,由於手機端螢幕太小,需求方希望可以弄一個全屏圖,把手機橫過來觀察曲線。
正常:
豎過來:
很容易想到的一種實現方法:設定曲線container的寬為螢幕的高,高為螢幕的寬,然後給曲線的container加一個transform:rotate(90deg)
,就能夠實現豎屏了。
這樣看起來沒問題,但是當需要展現tooltip的時候,會發現tooltip不好使了。表現出來的現象是:當使用者用手指延著曲線的x軸移動時,tooltip並不會跟著手指移動。
仔細觀察會發現,tooltip的移動,還是根據手指在螢幕上移動時的【橫座標】來的。
開始嘗試解決問題
經過查閱highcharts文件,發現chart.inverted
可以實現x軸和y軸的反轉。試了一下這個配置,發現並不是想象中那麼理想,主要原因為:
1、y軸在螢幕下方,但我們需要的是y軸在螢幕上方。而y軸的位置不那麼好調整。
2、tooltip也需要我們單獨進行旋轉。但是旋轉後,手指touchmove時,tooltip適配會出問題,有時候會跑到曲線外面去,無法控制。並且由於是svg畫圖,tooltip本身的transform-origin
的選擇就是一個比較蛋疼的問題。
查閱了highcharts其他的api,並且上git搜了搜issue,但是並沒有這個問題的解決方法。本來想放棄highcharts, 轉投echarts的, 但在echarts的issue裡發現很多人遇到了這個問題,但官方直接給列為了bug, 還不知道什麼時候可以修復。最後還是考慮使用highcharts。
沒辦法,改原始碼吧
由於大概知道問題產生的原因:圖表豎過來後,應該用觸控事件的縱座標而不是橫座標來作為表格內的橫座標。改原始碼應該挺快的。
專案是用透過npm安裝的highcharts, 首先根據highcharts的package.json可以看到,引用的入口檔案為:highcharts.js
這個檔案是壓縮過的,它對應的原始碼檔案為:highcharts.src.js
結合原始碼檔案和瀏覽器除錯,發現在使用者滑動手指是,進入了下面的邏輯:
onContainerTouchStart: function(e) {
this.zoomOption(e);
this.touch(e, true);
},
onContainerTouchMove: function(e) {
this.touch(e);
},
一切都在這個touch函式里。繼續往下看:
/**
* General touch handler shared by touchstart and touchmove.
*/
touch: function(e, start) {
var chart = this.chart,
hasMoved,
pinchDown,
isInside;
if (chart.index !== H.hoverChartIndex) {
this.onContainerMouseLeave({
relatedTarget: true
});
}
H.hoverChartIndex = chart.index;
// 這裡判斷是是否是單觸
if (e.touches.length === 1) {
// e為js的觸控事件
e = this.normalize(e);
// e.chartX ,e.chartY應該就是觸控時,在表格內部的橫座標和縱座標,這裡的normalize應該是對事件進行了擴充套件。
isInside = chart.isInsidePlot(
e.chartX - chart.plotLeft,
e.chartY - chart.plotTop
);
if (isInside && !chart.openMenu) {
// Run mouse events and display tooltip etc
if (start) {
this.runPointActions(e);
}
// Android fires touchmove events after the touchstart even if the
// finger hasn't moved, or moved only a pixel or two. In iOS however,
// the touchmove doesn't fire unless the finger moves more than ~4px.
// So we emulate this behaviour in Android by checking how much it
// moved, and cancelling on small distances. #3450.
if (e.type === 'touchmove') {
pinchDown = this.pinchDown;
hasMoved = pinchDown[0] ? Math.sqrt( // #5266
Math.pow(pinchDown[0].chartX - e.chartX, 2) +
Math.pow(pinchDown[0].chartY - e.chartY, 2)
) >= 4 : false;
}
if (pick(hasMoved, true)) {
this.pinch(e);
}
} else if (start) {
// Hide the tooltip on touching outside the plot area (#1203)
this.reset();
}
} else if (e.touches.length === 2) {
this.pinch(e);
}
},
這裡的normalize
比較關鍵,因為產生tooltip表現不符合預期的原因應該就是:旋轉時,由於圖被豎過來了,需要使用觸控事件的縱座標來表示觸控的【橫座標】,但highcharts依然採用了事件的橫座標。繼續看看normalize
的內容。
/**
* Takes a browser event object and extends it with custom Highcharts
* properties `chartX` and `chartY` in order to work on the internal
* coordinate system.
*
* @param {Object} e
* The event object in standard browsers.
*
* @return {PointerEvent}
* A browser event with extended properties `chartX` and `chartY`.
*/
normalize: function(e, chartPosition) {
var chartX,
chartY,
ePos;
// IE normalizing
e = e || win.event;
if (!e.target) {
e.target = e.srcElement;
}
// iOS (#2757)
ePos = e.touches ? (e.touches.length ? e.touches.item(0) : e.changedTouches[0]) : e;
// Get mouse position
if (!chartPosition) {
this.chartPosition = chartPosition = offset(this.chart.container);
}
// chartX and chartY
// 這裡是計算chartX和chartY,計算方法是用當前觸控(點選)事件的橫座標減去圖表的橫座標,縱座標減去圖表的縱座標。
if (ePos.pageX === undefined) { // IE < 9. #886.
chartX = Math.max(e.x, e.clientX - chartPosition.left); // #2005, #2129: the second case is
// for IE10 quirks mode within framesets
chartY = e.y;
} else {
chartX = ePos.pageX - chartPosition.left;
chartY = ePos.pageY - chartPosition.top;
}
// 這裡的chartX和chartY已經是在表格內部的座標了,後續的邏輯都是根據這個chartX和chartY來的,如果當圖表被豎過來時,人為把這兩個值交換一下,是否就解決了tooltip的問題呢。
if(豎屏) {
// 【豎屏】可以透過在配置表格的時候增加變數來判斷
return extend(e, {
chartX: Math.round(chartY),
chartY: Math.round(chartX)
});
}
return extend(e, {
chartX: Math.round(chartX),
chartY: Math.round(chartY)
});
},
按照這個思路,找到入口檔案highcharts.js裡對應部分的程式碼,進行修改。修改完成後,發現tooltip的表現符合預期了。
經過測試, 圖表的其他基本功能正常。之前還擔心這樣會不會影響lengends的點選等。後來想了一下,由於是svg畫圖,所有點選事件應該都是直接繫結在元素上的,而不是像canvas一樣強依賴座標,所以不會影響。這也是為什麼在echarts下進行rotate, lengends的互動也會受影響的原因。
由於highcharts的功能太多,這樣的原始碼修改功能對其他的功能有無影響,還不能完全確定。待後續繼續補充。