light-chart
Version:
Charts for mobile visualization.
315 lines (271 loc) • 9.91 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<title>带旋转的饼图</title>
<link rel="stylesheet" href="./assets/common.css">
<style>
.wrapper {
position: relative;
height: 100%;
}
canvas {
display: block;
}
.flag {
position: absolute;
bottom: 30px;
width: 24px;
height: 24px;
left: 50%;
margin-left: -12px;
background-image: url("https://gw.alipayobjects.com/zos/rmsportal/eCukcsfJoNgSIvKtFniY.png")
}
#content {
width: 100%;
text-align: center;
}
</style>
</head>
<body>
<div>
<div class="wrapper">
<canvas id="pieRotate"></canvas>
<div class="flag"></div>
</div>
<div id="content"></div>
</div>
<script src="./assets/jquery-3.2.1.min.js"></script>
<script src="../build/f2-all.js"></script>
<script src="./assets/hammer.js"></script>
<script>
// 绘制饼图
const { Util, G, Chart } = F2;
const Matrix = G.Matrix;
const data = [
{name: 'A',proportion: 0.8,a: '1'},
{name: 'B',proportion: 0.5,a: '1'},
{name: 'C',proportion: 0.45,a: '1'},
{name: 'D',proportion: 0.15,a: '1'},
{name: 'E',proportion: 0.12,a: '1' },
{name: 'F',proportion: 0.1, a: '1' },
{name: 'G',proportion: 0.03,a: '1'}
];
const chart = new Chart({
id: 'pieRotate',
width: window.innerWidth,
height: window.innerWidth * 0.64,
pixelRatio: window.devicePixelRatio
});
chart.source(data);
chart.legend(false);
chart.coord('polar', {
transposed: true,
radius: 0.8
});
chart.axis(false);
chart.tooltip(false);
const geom = chart.interval()
.position('a*proportion')
.color('name', ['#1890FF', '#41D9C7', '#2FC25B', '#FACC14', '#E6965C', '#223273', '#7564CC', '#8543E0', '#5C8EE6', '#13C2C2', '#5CA3E6'])
.adjust('stack')
.style({
lineWidth: 1,
stroke: '#fff'
});
chart.render();
// 开始交互逻辑
// 一些辅助工具类函数
const center = chart.get('coord').center; // 极坐标的圆点
const container = geom.get('container');
const canvas = chart.get('canvas');
const el = canvas.get('el');
let animating = false; // 动画状态标识位
function interpolateNumber(a, b) {
a = +a;
b -= a;
return function (t) {
return a + b * t;
};
}
function findShapeByName(name) {
let result;
const shapes = geom.get('container').get('children');
Util.each(shapes, shape => {
const origin = shape.get('origin')._origin;
if (origin.name === name) {
result = shape;
return false;
}
});
return result;
}
function repaintChart(angle) {
const chartCoord = chart.get('coord');
const coordStart = chartCoord.startAngle + angle;
const coordEnd = chartCoord.endAngle + angle;
chart.coord('polar', {
transposed: true,
radius: 0.8,
startAngle: coordStart,
endAngle: coordEnd
});
chart.animate(false);
chart.repaint();
}
function animation(angle, data, animateCfg) {
const rotateAngle = Math.PI / 2 - angle;
const diff = interpolateNumber(0, rotateAngle);
if (rotateAngle !== 0) {
container.animate().to(animateCfg).onFrame(t => {
animating = true;
const frameAngle = diff(t);
container.setTransform([
['t', center.x, center.y],
['r', frameAngle],
['t', -center.x, -center.y]
]);
if (t === 1) {
animating = false;
repaintChart(rotateAngle);
$('#content').html(data._origin.name + ': ' + data._origin.proportion);
}
});
}
}
let lastMouseAngle;
let totalAngle;
chart.pluginGesture({
options: {
useCalculate: false, // 计算手势数据点,如果不需要可以关闭提高性能
useOffset: false // 计算数据是否需要计算图表相对页面偏移的坐标。当图表宽度超出, scroll模式,计算位置需要加上滚动记录
},
gesture: {
tap: (data, e) => {
if (animating) return;
const currentPoint = e.center;
const record = chart.getSnapRecords(currentPoint)[0];
if (!record) return;
const shape = findShapeByName(record._origin.name);
if (shape) {
const startAngle = shape.attr('startAngle');
const endAngle = shape.attr('endAngle');
let middleAngle = (startAngle + endAngle) / 2;
if (startAngle > endAngle && endAngle <= 0) {
middleAngle = (Math.PI * 2 - Math.abs(startAngle - endAngle)) / 2 + startAngle;
if (middleAngle > 1.5 * Math.PI) {
middleAngle = middleAngle - 2 * Math.PI;
}
}
animation(middleAngle, record, {
duration: 650,
easing: 'backOut'
});
}
},
'panstart panmove panend': (data, e) => {
if (animating) return;
const currentPoint = e.center;
const newMouseAngle = Math.atan2(currentPoint.y - center.y, currentPoint.x - center.x) + Math.PI;
if (e.type === 'panstart') {
lastMouseAngle = newMouseAngle;
totalAngle = 0;
} else if (e.type === 'panmove') {
const diffAngle = newMouseAngle - lastMouseAngle;
totalAngle += diffAngle;
container.transform([
['t', center.x, center.y],
['r', diffAngle],
['t', -center.x, -center.y]
]);
canvas.draw();
} else if (e.type === 'panend') {
repaintChart(totalAngle);
const shapes = container.get('children');
let middleAngle = Math.PI * 0.5;
let rotateShape;
Util.each(shapes, s => {
const startAngle = s.attr('startAngle');
const endAngle = s.attr('endAngle');
if (startAngle <= Math.PI * 0.5 && endAngle >= Math.PI * 0.5) {
rotateShape = s;
middleAngle = (startAngle + endAngle) / 2;
return false;
}
});
animation(middleAngle, rotateShape.get('origin'), {
duration: 250,
easing: 'backOut'
});
}
lastMouseAngle = newMouseAngle;
}
}
});
/*
const hammer = new Hammer(el);
// 点击操作
hammer.on('tap', e => {
if (animating) return;
const currentPoint = e.center;
const record = chart.getSnapRecords(currentPoint)[0];
if (!record) return;
const shape = findShapeByName(record._origin.name);
if (shape) {
const startAngle = shape.attr('startAngle');
const endAngle = shape.attr('endAngle');
let middleAngle = (startAngle + endAngle) / 2;
if (startAngle > endAngle && endAngle <= 0) {
middleAngle = (Math.PI * 2 - Math.abs(startAngle - endAngle)) / 2 + startAngle;
if (middleAngle > 1.5 * Math.PI) {
middleAngle = middleAngle - 2 * Math.PI;
}
}
animation(middleAngle, {
duration: 650,
easing: 'backOut'
});
}
});
let lastMouseAngle;
let totalAngle;
hammer.on('panstart panmove panend', e => {
if (animating) return;
const currentPoint = e.center;
const newMouseAngle = Math.atan2(currentPoint.y - center.y, currentPoint.x - center.x) + Math.PI;
if (e.type === 'panstart') {
lastMouseAngle = newMouseAngle;
totalAngle = 0;
} else if (e.type === 'panmove') {
const diffAngle = newMouseAngle - lastMouseAngle;
totalAngle += diffAngle;
container.transform([
['t', center.x, center.y],
['r', diffAngle],
['t', -center.x, -center.y]
]);
canvas.draw();
} else if (e.type === 'panend') {
repaintChart(totalAngle);
const shapes = container.get('children');
let middleAngle = Math.PI * 0.5;
Util.each(shapes, s => {
const startAngle = s.attr('startAngle');
const endAngle = s.attr('endAngle');
if (startAngle <= Math.PI * 0.5 && endAngle >= Math.PI * 0.5) {
rotateShape = s;
middleAngle = (startAngle + endAngle) / 2;
return false;
}
});
animation(middleAngle, {
duration: 350,
easing: 'backOut'
});
}
lastMouseAngle = newMouseAngle;
});
*/
</script>
</body>
</html>