Init
This commit is contained in:
commit
5232019b84
5 changed files with 276 additions and 0 deletions
57
background.js
Normal file
57
background.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// Dynamic Icon generator
|
||||||
|
function createIcon(text, size = 16) {
|
||||||
|
const canvas = new OffscreenCanvas(size, size);
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
// Wipe canvas
|
||||||
|
ctx.clearRect(0, 0, size, size);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
ctx.fillStyle = "#4285F4"; // Google Blue
|
||||||
|
ctx.fillRect(0, 0, size, size);
|
||||||
|
|
||||||
|
// Text
|
||||||
|
ctx.fillStyle = "white";
|
||||||
|
ctx.font = `bold ${Math.floor(size * 0.5625)}px Arial`;
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.textBaseline = "middle";
|
||||||
|
ctx.fillText(text, size / 2, size / 2);
|
||||||
|
|
||||||
|
return ctx.getImageData(0, 0, size, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the extension icon
|
||||||
|
function updateExtensionIcon(zoomLevel, accelerationMultiplier) {
|
||||||
|
const zoomText = zoomLevel.toFixed(2);
|
||||||
|
const accelText = accelerationMultiplier.toFixed(2);
|
||||||
|
|
||||||
|
// Create icons of different sizes
|
||||||
|
const icon16 = createIcon(zoomText, 16);
|
||||||
|
const icon32 = createIcon(zoomText, 32);
|
||||||
|
const icon48 = createIcon(zoomText, 48);
|
||||||
|
const icon128 = createIcon(zoomText, 128);
|
||||||
|
|
||||||
|
// Set the icon
|
||||||
|
chrome.action.setIcon({
|
||||||
|
imageData: {
|
||||||
|
16: icon16,
|
||||||
|
32: icon32,
|
||||||
|
48: icon48,
|
||||||
|
128: icon128,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update badge with multiplier
|
||||||
|
chrome.action.setBadgeText({ text: accelText + "x" });
|
||||||
|
chrome.action.setBadgeBackgroundColor({ color: "#34A853" }); // Google Green
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for messages
|
||||||
|
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||||
|
if (request.action === "updateZoomInfo") {
|
||||||
|
updateExtensionIcon(request.zoomLevel, request.accelerationMultiplier);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set icon
|
||||||
|
updateExtensionIcon(1, 1);
|
93
content.js
Normal file
93
content.js
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
let zoomLevel = 1;
|
||||||
|
let lastGestureTime = 0;
|
||||||
|
let lastGestureScale = 1;
|
||||||
|
const minZoom = 0.1;
|
||||||
|
const maxZoom = 5;
|
||||||
|
let accelerationCurve = [1, 1, 1, 1, 1];
|
||||||
|
|
||||||
|
function interpolateAcceleration(speed) {
|
||||||
|
const index = Math.min(
|
||||||
|
Math.floor((speed * (accelerationCurve.length - 1)) / 2),
|
||||||
|
accelerationCurve.length - 2,
|
||||||
|
);
|
||||||
|
const t = ((speed * (accelerationCurve.length - 1)) / 2) % 1;
|
||||||
|
return accelerationCurve[index] * (1 - t) + accelerationCurve[index + 1] * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyZoom(delta, speed) {
|
||||||
|
const accelerationMultiplier = interpolateAcceleration(speed);
|
||||||
|
const zoomChange = delta * accelerationMultiplier;
|
||||||
|
zoomLevel *= 1 + zoomChange;
|
||||||
|
zoomLevel = Math.min(Math.max(zoomLevel, minZoom), maxZoom);
|
||||||
|
|
||||||
|
document.body.style.transform = `scale(${zoomLevel})`;
|
||||||
|
document.body.style.transformOrigin = "center top";
|
||||||
|
|
||||||
|
// Update Icon via Messages
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
action: "updateZoomInfo",
|
||||||
|
zoomLevel: zoomLevel,
|
||||||
|
accelerationMultiplier: accelerationMultiplier,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener(
|
||||||
|
"wheel",
|
||||||
|
(event) => {
|
||||||
|
if (event.ctrlKey || event.metaKey) {
|
||||||
|
event.preventDefault();
|
||||||
|
const currentTime = performance.now();
|
||||||
|
const timeDiff = (currentTime - lastGestureTime) / 1000;
|
||||||
|
const delta = event.deltaY * -0.001;
|
||||||
|
const speed = Math.abs(delta / timeDiff);
|
||||||
|
applyZoom(delta, speed);
|
||||||
|
lastGestureTime = currentTime;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ passive: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
window.addEventListener(
|
||||||
|
"gesturestart",
|
||||||
|
(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
lastGestureScale = 1;
|
||||||
|
lastGestureTime = performance.now();
|
||||||
|
},
|
||||||
|
{ passive: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
window.addEventListener(
|
||||||
|
"gesturechange",
|
||||||
|
(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const currentTime = performance.now();
|
||||||
|
const timeDiff = (currentTime - lastGestureTime) / 1000;
|
||||||
|
const delta = event.scale - lastGestureScale;
|
||||||
|
const speed = Math.abs(delta / timeDiff);
|
||||||
|
applyZoom(delta, speed);
|
||||||
|
lastGestureScale = event.scale;
|
||||||
|
lastGestureTime = currentTime;
|
||||||
|
},
|
||||||
|
{ passive: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
window.addEventListener(
|
||||||
|
"gestureend",
|
||||||
|
(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
{ passive: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||||
|
if (request.action === "updateSettings") {
|
||||||
|
accelerationCurve = request.settings;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
chrome.storage.sync.get("accelerationCurve", (data) => {
|
||||||
|
if (data.accelerationCurve) {
|
||||||
|
accelerationCurve = data.accelerationCurve;
|
||||||
|
}
|
||||||
|
});
|
20
manifest.json
Normal file
20
manifest.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
|
"name": "Zoom Accelerator",
|
||||||
|
"version": "1.0",
|
||||||
|
"description": "Adds customizable smooth zoom acceleration to Chrome",
|
||||||
|
"permissions": ["activeTab", "storage"],
|
||||||
|
"host_permissions": ["<all_urls>"],
|
||||||
|
"action": {
|
||||||
|
"default_popup": "popup.html"
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"service_worker": "background.js"
|
||||||
|
},
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": ["<all_urls>"],
|
||||||
|
"js": ["content.js"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
31
popup.html
Normal file
31
popup.html
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
width: 300px;
|
||||||
|
padding: 10px;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
.slider-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.slider {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
width: 40px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>Zoom Acceleration Curve</h2>
|
||||||
|
<div id="sliders"></div>
|
||||||
|
<script src="popup.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
75
popup.js
Normal file
75
popup.js
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
const numSliders = 5;
|
||||||
|
const defaultMultiplier = 1;
|
||||||
|
|
||||||
|
function createSliders() {
|
||||||
|
const slidersContainer = document.getElementById("sliders");
|
||||||
|
for (let i = 0; i < numSliders; i++) {
|
||||||
|
const sliderContainer = document.createElement("div");
|
||||||
|
sliderContainer.className = "slider-container";
|
||||||
|
|
||||||
|
const label = document.createElement("label");
|
||||||
|
label.textContent = `${(i / (numSliders - 1) * 2).toFixed(2)} zoom units/s`;
|
||||||
|
|
||||||
|
const slider = document.createElement("input");
|
||||||
|
slider.type = "range";
|
||||||
|
slider.min = "1";
|
||||||
|
slider.max = "10";
|
||||||
|
slider.step = "0.1";
|
||||||
|
slider.value = defaultMultiplier;
|
||||||
|
slider.className = "slider";
|
||||||
|
|
||||||
|
const value = document.createElement("span");
|
||||||
|
value.className = "value";
|
||||||
|
value.textContent = `${defaultMultiplier}x`;
|
||||||
|
|
||||||
|
sliderContainer.appendChild(label);
|
||||||
|
sliderContainer.appendChild(slider);
|
||||||
|
sliderContainer.appendChild(value);
|
||||||
|
slidersContainer.appendChild(sliderContainer);
|
||||||
|
|
||||||
|
slider.addEventListener("input", () => {
|
||||||
|
value.textContent = `${parseFloat(slider.value).toFixed(1)}x`;
|
||||||
|
saveSettings();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveSettings() {
|
||||||
|
const sliders = document.querySelectorAll(".slider");
|
||||||
|
const settings = Array.from(sliders).map((slider) => parseFloat(slider.value));
|
||||||
|
chrome.storage.sync.set({ accelerationCurve: settings }, () => {
|
||||||
|
sendSettingsToActiveTab(settings);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendSettingsToActiveTab(settings) {
|
||||||
|
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||||
|
if (tabs[0]) {
|
||||||
|
chrome.tabs.sendMessage(
|
||||||
|
tabs[0].id,
|
||||||
|
{ action: "updateSettings", settings },
|
||||||
|
(response) => {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
console.log("Could not send settings to content script.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSettings() {
|
||||||
|
chrome.storage.sync.get("accelerationCurve", (data) => {
|
||||||
|
if (data.accelerationCurve) {
|
||||||
|
const sliders = document.querySelectorAll(".slider");
|
||||||
|
data.accelerationCurve.forEach((value, index) => {
|
||||||
|
sliders[index].value = value;
|
||||||
|
sliders[index].nextElementSibling.textContent = `${value.toFixed(1)}x`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createSliders();
|
||||||
|
loadSettings();
|
||||||
|
|
Loading…
Add table
Reference in a new issue