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