fix: increase touch size for sensors, remove flicker on drag
This commit is contained in:
+100
-58
@@ -351,74 +351,109 @@ function _onRoomDragEnd(room, group) {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function renderSensors() {
|
function renderSensors() {
|
||||||
sensorsLayer.destroyChildren();
|
const activeIds = new Set(
|
||||||
|
sensors
|
||||||
|
.filter(s => s.room_id != null && s.floor_x != null)
|
||||||
|
.map(s => `sensor-${s.id}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove groups for sensors that are now unplaced or gone.
|
||||||
|
sensorsLayer.find('Group').forEach(g => {
|
||||||
|
if (!activeIds.has(g.id())) g.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
sensors.forEach(sensor => {
|
sensors.forEach(sensor => {
|
||||||
if (sensor.room_id == null || sensor.floor_x == null) return;
|
if (sensor.room_id == null || sensor.floor_x == null) return;
|
||||||
const room = rooms.find(r => r.id === sensor.room_id);
|
const room = rooms.find(r => r.id === sensor.room_id);
|
||||||
if (!room) return;
|
if (!room) return;
|
||||||
|
|
||||||
const absX = (room.x + sensor.floor_x) * PPM;
|
const absX = (room.x + sensor.floor_x) * PPM;
|
||||||
const absY = (room.y + sensor.floor_y) * PPM;
|
const absY = (room.y + sensor.floor_y) * PPM;
|
||||||
const label = sensor.name ?? sensor.sensor_id;
|
const label = sensor.name ?? sensor.sensor_id;
|
||||||
|
|
||||||
const group = new Konva.Group({
|
const existing = sensorsLayer.findOne(`#sensor-${sensor.id}`);
|
||||||
id: `sensor-${sensor.id}`,
|
if (existing) {
|
||||||
x: absX, y: absY,
|
// Update in-place — avoids the destroy-rebuild flash on drag end.
|
||||||
draggable: mode === 'sensorMove',
|
if (!existing.isDragging()) existing.position({ x: absX, y: absY });
|
||||||
});
|
existing.draggable(mode === 'sensorMove');
|
||||||
|
existing.findOne('Text')?.text(label);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
group.add(new Konva.Circle({
|
_buildSensorGroup(sensor, absX, absY, label);
|
||||||
radius: 8,
|
|
||||||
fill: '#1565C0',
|
|
||||||
stroke: '#ffffff',
|
|
||||||
strokeWidth: 2,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Highlight ring — shown by highlightSensor().
|
|
||||||
group.add(new Konva.Circle({
|
|
||||||
name: 'highlight-ring',
|
|
||||||
radius: 14,
|
|
||||||
stroke: '#FDD835',
|
|
||||||
strokeWidth: 3,
|
|
||||||
visible: false,
|
|
||||||
listening: false,
|
|
||||||
}));
|
|
||||||
|
|
||||||
group.add(new Konva.Text({
|
|
||||||
text: label,
|
|
||||||
fontSize: 10,
|
|
||||||
fill: '#424242',
|
|
||||||
y: 11,
|
|
||||||
offsetX: 30,
|
|
||||||
width: 60,
|
|
||||||
align: 'center',
|
|
||||||
listening: false,
|
|
||||||
}));
|
|
||||||
|
|
||||||
group.on('click tap', () => {
|
|
||||||
if (mode === 'view') {
|
|
||||||
notifyFlutter({ type: 'sensorTapped', id: String(sensor.id) });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let pressTimer = null;
|
|
||||||
group.on('mousedown touchstart', () => {
|
|
||||||
if (mode === 'view') return;
|
|
||||||
pressTimer = setTimeout(() => {
|
|
||||||
notifyFlutter({ type: 'sensorTapped', id: String(sensor.id) });
|
|
||||||
pressTimer = null;
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
group.on('mouseup touchend touchcancel dragstart', () => {
|
|
||||||
clearTimeout(pressTimer);
|
|
||||||
pressTimer = null;
|
|
||||||
});
|
|
||||||
group.on('dragend', () => _onSensorDragEnd(sensor, group, room));
|
|
||||||
sensorsLayer.add(group);
|
|
||||||
});
|
});
|
||||||
sensorsLayer.batchDraw();
|
sensorsLayer.batchDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _buildSensorGroup(sensor, absX, absY, label) {
|
||||||
|
const group = new Konva.Group({
|
||||||
|
id: `sensor-${sensor.id}`,
|
||||||
|
x: absX, y: absY,
|
||||||
|
draggable: mode === 'sensorMove',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Visible dot — hitFunc expands the touch target without changing appearance.
|
||||||
|
group.add(new Konva.Circle({
|
||||||
|
radius: 8,
|
||||||
|
fill: '#1565C0',
|
||||||
|
stroke: '#ffffff',
|
||||||
|
strokeWidth: 2,
|
||||||
|
hitFunc(ctx, shape) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(0, 0, 20, 0, Math.PI * 2, true);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fillStrokeShape(shape);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Highlight ring — shown by highlightSensor().
|
||||||
|
group.add(new Konva.Circle({
|
||||||
|
name: 'highlight-ring',
|
||||||
|
radius: 14,
|
||||||
|
stroke: '#FDD835',
|
||||||
|
strokeWidth: 3,
|
||||||
|
visible: false,
|
||||||
|
listening: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
group.add(new Konva.Text({
|
||||||
|
text: label,
|
||||||
|
fontSize: 10,
|
||||||
|
fill: '#424242',
|
||||||
|
y: 11,
|
||||||
|
offsetX: 30,
|
||||||
|
width: 60,
|
||||||
|
align: 'center',
|
||||||
|
listening: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
group.on('click tap', () => {
|
||||||
|
if (mode === 'view') {
|
||||||
|
notifyFlutter({ type: 'sensorTapped', id: String(sensor.id) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let pressTimer = null;
|
||||||
|
group.on('mousedown touchstart', () => {
|
||||||
|
if (mode === 'view') return;
|
||||||
|
pressTimer = setTimeout(() => {
|
||||||
|
notifyFlutter({ type: 'sensorTapped', id: String(sensor.id) });
|
||||||
|
pressTimer = null;
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
group.on('mouseup touchend touchcancel dragstart', () => {
|
||||||
|
clearTimeout(pressTimer);
|
||||||
|
pressTimer = null;
|
||||||
|
});
|
||||||
|
// Look up the current sensor data at dragend so room_id is always fresh.
|
||||||
|
group.on('dragend', () => {
|
||||||
|
const s = sensors.find(ss => ss.id === sensor.id) ?? sensor;
|
||||||
|
const originalRoom = rooms.find(r => r.id === s.room_id) ?? rooms[0];
|
||||||
|
_onSensorDragEnd(s, group, originalRoom);
|
||||||
|
});
|
||||||
|
sensorsLayer.add(group);
|
||||||
|
}
|
||||||
|
|
||||||
function _onSensorDragEnd(sensor, group, originalRoom) {
|
function _onSensorDragEnd(sensor, group, originalRoom) {
|
||||||
const newAbsX = group.x() / PPM;
|
const newAbsX = group.x() / PPM;
|
||||||
const newAbsY = group.y() / PPM;
|
const newAbsY = group.y() / PPM;
|
||||||
@@ -427,12 +462,19 @@ function _onSensorDragEnd(sensor, group, originalRoom) {
|
|||||||
newAbsY >= r.y && newAbsY <= r.y + r.height,
|
newAbsY >= r.y && newAbsY <= r.y + r.height,
|
||||||
) ?? originalRoom;
|
) ?? originalRoom;
|
||||||
|
|
||||||
|
// Optimistically update the local sensors array so any renderSensors()
|
||||||
|
// call that arrives before Flutter confirms the new position via loadSensors
|
||||||
|
// doesn't snap the group back to the old coordinates.
|
||||||
|
sensor.room_id = target.id;
|
||||||
|
sensor.floor_x = newAbsX - target.x;
|
||||||
|
sensor.floor_y = newAbsY - target.y;
|
||||||
|
|
||||||
notifyFlutter({
|
notifyFlutter({
|
||||||
type: 'sensorMoved',
|
type: 'sensorMoved',
|
||||||
id: String(sensor.id),
|
id: String(sensor.id),
|
||||||
roomId: target.id,
|
roomId: target.id,
|
||||||
x: newAbsX - target.x,
|
x: sensor.floor_x,
|
||||||
y: newAbsY - target.y,
|
y: sensor.floor_y,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user