|
|
library flutterptcontrol;
|
|
|
|
|
|
import 'dart:math' as MATH;
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
|
typedef void PtCallback(int status);
|
|
|
|
|
|
//pan-tilt Control
|
|
|
class PtControlWidget extends StatefulWidget {
|
|
|
PtControlWidget(
|
|
|
{@required this.innerRadius, @required this.outerRadius, @required this.callback});
|
|
|
|
|
|
///内径
|
|
|
final double innerRadius;
|
|
|
|
|
|
///外径
|
|
|
final double outerRadius;
|
|
|
|
|
|
///点击回调
|
|
|
final PtCallback callback;
|
|
|
|
|
|
@override
|
|
|
State<StatefulWidget> createState() {
|
|
|
return _PtControlWidgetState();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
class _PtControlWidgetState extends State<PtControlWidget> {
|
|
|
int status = -1;
|
|
|
|
|
|
/*
|
|
|
3.获取控件的坐标:
|
|
|
RenderBox renderBox = anchorKey.currentContext.findRenderObject();
|
|
|
var offset = renderBox.localToGlobal(Offset.zero);
|
|
|
控件的横坐标:offset.dx
|
|
|
控件的纵坐标:offset.dy
|
|
|
|
|
|
如果想获得控件正下方的坐标:
|
|
|
RenderBox renderBox = anchorKey.currentContext.findRenderObject();
|
|
|
var offset = renderBox.localToGlobal(Offset(0.0, renderBox.size.height));
|
|
|
控件下方的横坐标:offset.dx
|
|
|
控件下方的纵坐标:offset.dy
|
|
|
————————————————
|
|
|
版权声明:本文为CSDN博主「笨鸟不飞 ≧0≦」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
|
|
|
原文链接:https://blog.csdn.net/baidu_34120295/article/details/86495861
|
|
|
*/
|
|
|
|
|
|
//获得本组件中心点对象:Point(offset.dx, offset.dy)
|
|
|
//Another exception was thrown: type 'Point<num>' is not a subtype of type 'Point<double>'
|
|
|
MATH.Point getCenterPoint() {
|
|
|
RenderBox renderBox = context.findRenderObject();
|
|
|
//控件中心坐标:(offset.dx, offset.dy)
|
|
|
var offset =
|
|
|
renderBox.localToGlobal(Offset(renderBox.size.width / 2, renderBox.size.height / 2));
|
|
|
print('(offset.dx, offset.dy) = (${offset.dx}, ${offset.dy})');
|
|
|
return MATH.Point<num>(offset.dx.toInt(), offset.dy.toInt());
|
|
|
}
|
|
|
|
|
|
@override
|
|
|
Widget build(BuildContext context) {
|
|
|
return GestureDetector(
|
|
|
onPanDown: (details) {
|
|
|
//坐标系转换
|
|
|
var x = details.localPosition.dx - widget.outerRadius;
|
|
|
var y = -(details.localPosition.dy - widget.outerRadius);
|
|
|
|
|
|
//判断点击位置是否超出范围
|
|
|
var dis = MATH.Point(x, y).distanceTo(MATH.Point(0, 0));
|
|
|
|
|
|
/*
|
|
|
计算圆周上坐标点的公式,其中圆心坐标(xo,yo),r为圆半径,α为0~2π,(x,y)为圆上的点
|
|
|
x = xo + r * cos(α)
|
|
|
y = yo + r * sin(α)
|
|
|
|
|
|
原点坐标为(0, 0),x轴向左为正,y轴向上为正,逆时针方向的8个点的弧度为
|
|
|
0 π * 1/8
|
|
|
1 π * 3/8
|
|
|
2 π * 5/8
|
|
|
3 π * 7/8
|
|
|
4 π * 9/8
|
|
|
5 π * 11/8
|
|
|
6 π * 13/8
|
|
|
7 π * 15/8
|
|
|
*/
|
|
|
|
|
|
// dart 计算原点坐标为(0, 0),x轴向左为正,y轴向上为正,逆时针方向的8个点的坐标
|
|
|
List listInnerPoint = List.generate(8, (index) {
|
|
|
double _radians = MATH.pi * (index * 2 + 1) / 8;
|
|
|
//print('index = $index');
|
|
|
return {
|
|
|
'x': MATH.cos(_radians) * widget.innerRadius,
|
|
|
'y': MATH.sin(_radians) * widget.innerRadius,
|
|
|
};
|
|
|
});
|
|
|
|
|
|
// dart 计算原点坐标为(0, 0),x轴向左为正,y轴向上为正,逆时针方向的8个点的坐标
|
|
|
List listOuterPoint = List.generate(8, (index) {
|
|
|
double _radians = MATH.pi * (index * 2 + 1) / 8;
|
|
|
//print('index = $index');
|
|
|
return {
|
|
|
'x': MATH.cos(_radians) * widget.outerRadius,
|
|
|
'y': MATH.sin(_radians) * widget.outerRadius,
|
|
|
};
|
|
|
});
|
|
|
|
|
|
print('listInnerPoint = ${listInnerPoint}');
|
|
|
print('[x, y] = [$x, $y]');
|
|
|
//超出范围
|
|
|
if (dis > widget.outerRadius) {
|
|
|
status = -1;
|
|
|
} else if (dis < widget.innerRadius) {
|
|
|
//还原
|
|
|
status = 8;
|
|
|
} else if (x > listOuterPoint[1]['x'] ||
|
|
|
(y > 0 &&
|
|
|
x > listInnerPoint[1]['x'] &&
|
|
|
y <
|
|
|
listInnerPoint[1]['y'] +
|
|
|
(x - listInnerPoint[1]['x']) / MATH.tan(MATH.pi / 8)) ||
|
|
|
(y < 0 &&
|
|
|
x > listInnerPoint[6]['x'] &&
|
|
|
y >
|
|
|
listInnerPoint[6]['y'] -
|
|
|
(x - listInnerPoint[6]['x']) / MATH.tan(MATH.pi / 8))) {
|
|
|
if (y > listOuterPoint[0]['y'] ||
|
|
|
y > listInnerPoint[0]['y'] &&
|
|
|
x <
|
|
|
listInnerPoint[0]['x'] +
|
|
|
(y - listInnerPoint[0]['y']) / MATH.tan(MATH.pi / 8)) {
|
|
|
//右上
|
|
|
status = 1;
|
|
|
} else if (y > listInnerPoint[7]['y'] ||
|
|
|
y > listOuterPoint[7]['y'] &&
|
|
|
x >
|
|
|
listInnerPoint[7]['x'] +
|
|
|
(listInnerPoint[7]['y'] - y) / MATH.tan(MATH.pi / 8)) {
|
|
|
//右
|
|
|
status = 0;
|
|
|
} else {
|
|
|
//右下
|
|
|
status = 7;
|
|
|
}
|
|
|
} else if (x > listInnerPoint[2]['x'] ||
|
|
|
y > 0 &&
|
|
|
x > listOuterPoint[2]['x'] &&
|
|
|
y > listInnerPoint[2]['y'] + (listInnerPoint[2]['x'] - x) / MATH.tan(MATH.pi / 8) ||
|
|
|
y < 0 &&
|
|
|
x < listInnerPoint[5]['x'] &&
|
|
|
y < listInnerPoint[5]['y'] - (listInnerPoint[5]['x'] - x) / MATH.tan(MATH.pi / 8)) {
|
|
|
if (y > 0) {
|
|
|
//上
|
|
|
status = 2;
|
|
|
} else {
|
|
|
//下
|
|
|
status = 6;
|
|
|
}
|
|
|
} else {
|
|
|
if (y > listOuterPoint[3]['y'] ||
|
|
|
y > listInnerPoint[3]['y'] &&
|
|
|
x >
|
|
|
listOuterPoint[3]['x'] -
|
|
|
(y - listOuterPoint[3]['y']) / MATH.tan(MATH.pi / 8)) {
|
|
|
//左上
|
|
|
status = 3;
|
|
|
} else if (y > listInnerPoint[4]['y'] ||
|
|
|
y > listOuterPoint[4]['y'] &&
|
|
|
x <
|
|
|
listOuterPoint[4]['x'] +
|
|
|
(listInnerPoint[4]['y'] - y) / MATH.tan(MATH.pi / 8)) {
|
|
|
//左
|
|
|
status = 4;
|
|
|
} else {
|
|
|
//左下
|
|
|
status = 5;
|
|
|
}
|
|
|
}
|
|
|
widget.callback(status);
|
|
|
setState(() {});
|
|
|
},
|
|
|
onPanEnd: (details) {
|
|
|
status = -1;
|
|
|
setState(() {});
|
|
|
},
|
|
|
|
|
|
///点击回调
|
|
|
/// status == -1 超出范围
|
|
|
/// status == 0 右
|
|
|
/// status == 1 右上
|
|
|
/// status == 2 上
|
|
|
/// status == 3 左上
|
|
|
/// status == 4 左
|
|
|
/// status == 5 左下
|
|
|
/// status == 6 下
|
|
|
/// status == 7 右下
|
|
|
/// status == 8 还原
|
|
|
|
|
|
/// color: Colors.black12
|
|
|
child: Stack(
|
|
|
children: <Widget>[
|
|
|
Image.asset(
|
|
|
null == widget.callback
|
|
|
? 'images/pt_background_disable.png'
|
|
|
: 'images/pt_background_enable.png',
|
|
|
package: 'flutterptcontrol'),
|
|
|
status == 0
|
|
|
? Image.asset('images/pt_right.png', package: 'flutterptcontrol')
|
|
|
: SizedBox.shrink(),
|
|
|
status == 1
|
|
|
? Image.asset('images/pt_right_top.png', package: 'flutterptcontrol')
|
|
|
: SizedBox.shrink(),
|
|
|
status == 2
|
|
|
? Image.asset('images/pt_top.png', package: 'flutterptcontrol')
|
|
|
: SizedBox.shrink(),
|
|
|
status == 3
|
|
|
? Image.asset('images/pt_left_top.png', package: 'flutterptcontrol')
|
|
|
: SizedBox.shrink(),
|
|
|
status == 4
|
|
|
? Image.asset('images/pt_left.png', package: 'flutterptcontrol')
|
|
|
: SizedBox.shrink(),
|
|
|
status == 5
|
|
|
? Image.asset('images/pt_left_bottom.png', package: 'flutterptcontrol')
|
|
|
: SizedBox.shrink(),
|
|
|
status == 6
|
|
|
? Image.asset('images/pt_bottom.png', package: 'flutterptcontrol')
|
|
|
: SizedBox.shrink(),
|
|
|
status == 7
|
|
|
? Image.asset('images/pt_right_bottom.png', package: 'flutterptcontrol')
|
|
|
: SizedBox.shrink(),
|
|
|
// status == 8
|
|
|
// ? Image.asset('images/pt_center.png', package: 'flutterptcontrol')
|
|
|
// : SizedBox.shrink(),
|
|
|
],
|
|
|
),
|
|
|
);
|
|
|
}
|
|
|
}
|