-
Notifications
You must be signed in to change notification settings - Fork 158
/
focal.js
147 lines (125 loc) · 3.42 KB
/
focal.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
function inPercent(value, total) {
return (value / total) * 100;
}
function inNumber(percent, total) {
if (percent > 100) {
throw Error("Invalid number, percent can't be more than 100");
}
return (percent / 100) * total;
}
/**
* if any of the conditions are true, the position
* is outside the containment. As we want to know
* if the position is inside the container we
* reverse the result of the set of conditions.
*/
const insideContainment = (x, y, containment) => {
return !(
x < containment.x ||
x > containment.x + containment.width ||
y < containment.y ||
y > containment.y + containment.height
);
};
const makeFocalPoint = (
initialPos = { x: 50, y: 50 },
onChange = function () {
return undefined;
},
isReadOnly = false,
) => {
const originalImage = document.getElementById("originalImage");
const coords = document.getElementById("coords");
const dot = document.querySelector(".focal-point .dot");
const previews = document.querySelectorAll("[class*='preview-'] img");
let pos = initialPos;
let isDown = false;
const getImgRect = () => originalImage.getBoundingClientRect();
const updatePosition = (e) => {
const { x: imgX, y: imgY, height, width } = getImgRect();
const x = Math.round(inPercent(e.clientX - imgX, width));
const y = Math.round(inPercent(e.clientY - imgY, height));
if (x <= 0) {
pos.x = 0;
} else if (x >= 100) {
pos.x = 100;
} else {
pos.x = x;
}
if (y <= 0) {
pos.y = 0;
} else if (y >= 100) {
pos.y = 100;
} else {
pos.y = y;
}
};
const updateDot = () => {
const { width, height } = getImgRect();
dot.style.left = inNumber(pos.x, width) - dot.offsetWidth / 2 + "px";
dot.style.top = inNumber(pos.y, height) - dot.offsetHeight / 2 + "px";
};
const updateCoords = () => {
coords.innerText = JSON.stringify(pos);
};
const updatePreviews = () => {
previews.forEach((preview) => {
preview.style.objectPosition = `${pos.x}% ${pos.y}%`;
});
};
const insideImage = (e) => {
return insideContainment(e.clientX, e.clientY, getImgRect());
};
const handleImageDown = (e) => {
if (!insideImage(e)) {
return;
}
isDown = true;
updatePosition(e);
updateDot();
updatePreviews();
};
const handleImageUp = () => {
isDown = false;
onChange(pos);
};
const handleDotMove = (e) => {
if (!isDown) {
return;
}
updatePosition(e);
updateDot();
updatePreviews();
updateCoords();
};
const handleWindowResize = () => {
updateDot();
updatePreviews();
};
const init = () => {
window.addEventListener("resize", handleWindowResize);
if (!isReadOnly) {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "attributes") {
originalImage.addEventListener(
"load",
() => {
updateDot();
updatePreviews();
},
{ once: true },
);
}
});
});
observer.observe(originalImage, { attributeFilter: ["src"] });
document.addEventListener("mousedown", handleImageDown, true);
document.addEventListener("mouseup", handleImageUp, true);
document.addEventListener("mousemove", handleDotMove, true);
}
updateDot();
updatePreviews();
};
init();
};