Cool Calculator

Draggable, resizable, fullscreen scientific calculator with pink-purple gradient, animations, and full scientific functions by Leon Luk

当前为 2025-10-05 提交的版本,查看 最新版本

// ==UserScript==
// @name         Cool Calculator
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  Draggable, resizable, fullscreen scientific calculator with pink-purple gradient, animations, and full scientific functions by Leon Luk
// @author       Leon Luk
// @match        *://*/*
// @grant        none
// @license      Proprietary
// ==/UserScript==

(function() {
    'use strict';

    const STORAGE_POS = 'leonluk_calc_pos';
    const STORAGE_OPEN = 'leonluk_calc_open';
    const STORAGE_FULLSCREEN = 'leonluk_calc_fullscreen';
    const STORAGE_SCALE = 'leonluk_calc_scale';

    // Create the toggle button
    const button = document.createElement('button');
    button.innerText = '🧮 Calculator';
    Object.assign(button.style, {
        position: 'fixed',
        top: '50%',
        left: '10px',
        transform: 'translateY(-50%)',
        zIndex: '999999',
        padding: '10px 15px',
        fontSize: '14px',
        background: 'linear-gradient(135deg,#d946ef,#7c3aed)',
        color: 'white',
        border: 'none',
        borderRadius: '8px',
        cursor: 'pointer',
        boxShadow: '0 2px 8px rgba(0,0,0,0.3)',
        transition: 'opacity 0.3s ease'
    });
    button.onmouseenter = () => button.style.opacity = '0.8';
    button.onmouseleave = () => button.style.opacity = '1';
    document.body.appendChild(button);

    // Create calculator container
    const calc = document.createElement('div');
    Object.assign(calc.style, {
        position: 'fixed',
        top: '50%',
        left: '-350px',
        width: '320px',
        height: 'auto',
        padding: '15px',
        background: 'linear-gradient(135deg,#ec4899,#8b5cf6)',
        color: 'white',
        borderRadius: '10px',
        boxShadow: '4px 0 15px rgba(0,0,0,0.6)',
        zIndex: '1000000',
        transition: 'left 0.5s cubic-bezier(0.25,1,0.3,1), width 0.3s, height 0.3s',
        fontFamily: 'monospace',
        opacity: '0.97',
        cursor: 'grab',
        overflow: 'hidden'
    });
    document.body.appendChild(calc);

    const savedPos = JSON.parse(localStorage.getItem(STORAGE_POS));
    const savedOpen = localStorage.getItem(STORAGE_OPEN);
    const savedFullscreen = localStorage.getItem(STORAGE_FULLSCREEN);

    if (savedPos) {
        calc.style.left = savedPos.left + 'px';
        calc.style.top = savedPos.top + 'px';
        calc.style.width = (savedPos.width || 320) + 'px';
        calc.style.height = (savedPos.height || 'auto') + 'px';
        calc.style.transform = 'none';
    }

    // Calculator HTML
    calc.innerHTML = `
        <div id="calc-header" style="display:flex;justify-content:space-between;align-items:center;font-weight:bold;margin-bottom:5px;cursor:move;font-size:16px;">
            <span>Calculator</span>
            <div>
                <button id="fullscreen-btn" style="background:none;border:none;color:white;font-size:18px;cursor:pointer;line-height:1;padding:0;margin-right:5px;">🗖</button>
                <button id="minimize-btn" style="background:none;border:none;color:white;font-size:20px;cursor:pointer;line-height:1;padding:0;">—</button>
            </div>
        </div>
        <input type="text" id="calc-display" readonly style="width:100%;padding:10px;font-size:18px;border:none;border-radius:5px;text-align:right;background:rgba(0,0,0,0.3);color:white;margin-bottom:5px;">
        <div id="calc-buttons" style="display:grid;grid-template-columns:repeat(6,1fr);gap:5px;margin-bottom:5px;"></div>
        <div style="text-align:right;font-size:10px;opacity:0.8;">by Leon Luk</div>
    `;

    const display = calc.querySelector('#calc-display');
    const buttonsContainer = calc.querySelector('#calc-buttons');
    const minimizeBtn = calc.querySelector('#minimize-btn');
    const fullscreenBtn = calc.querySelector('#fullscreen-btn');

    // Scientific buttons
    const buttons = [
        '7','8','9','/','C','(', 
        '4','5','6','*','^',')', 
        '1','2','3','-','√','x²',
        '0','.','=','+','%','+/-',
        'sin','cos','tan','asin','acos','atan',
        'log','ln','pi','e'
    ];

    buttons.forEach(key => {
        const btn = document.createElement('button');
        btn.innerText = key;
        Object.assign(btn.style, {
            padding: '10px',
            fontSize: '14px',
            border: 'none',
            borderRadius: '5px',
            background: 'rgba(255,255,255,0.15)',
            color: 'white',
            cursor: 'pointer',
            transition: 'background 0.2s ease, transform 0.1s ease'
        });
        btn.onmouseenter = () => btn.style.background = 'rgba(255,255,255,0.25)';
        btn.onmouseleave = () => btn.style.background = 'rgba(255,255,255,0.15)';
        btn.onmousedown = () => btn.style.transform = 'scale(0.95)';
        btn.onmouseup = () => btn.style.transform = 'scale(1)';

        btn.addEventListener('click', () => {
            try {
                if(key==='C'){ display.value=''; }
                else if(key==='='){
                    let expr = display.value
                        .replace(/\^/g,'**')
                        .replace(/√/g,'Math.sqrt')
                        .replace(/pi/g,'Math.PI')
                        .replace(/e/g,'Math.E')
                        .replace(/sin/g,'Math.sin')
                        .replace(/cos/g,'Math.cos')
                        .replace(/tan/g,'Math.tan')
                        .replace(/asin/g,'Math.asin')
                        .replace(/acos/g,'Math.acos')
                        .replace(/atan/g,'Math.atan')
                        .replace(/log/g,'Math.log10')
                        .replace(/ln/g,'Math.log');
                    display.value = eval(expr);
                }
                else if(key==='x²'){ display.value += '**2'; }
                else if(key==='+/-'){ display.value = display.value.startsWith('-') ? display.value.slice(1) : '-' + display.value; }
                else { display.value += key; }
            } catch {
                display.value = 'Error';
            }
        });
        buttonsContainer.appendChild(btn);
    });

    // Open/Close and Fullscreen logic
    let isOpen = false;
    if(savedOpen === 'true'){ isOpen = true; calc.style.left = '70px'; }
    let isFullscreen = false;
    if(savedFullscreen === 'true'){ 
        isFullscreen = true;
        calc.style.left='0';
        calc.style.top='0';
        calc.style.width=window.innerWidth+'px';
        calc.style.height=window.innerHeight+'px';
    }

    button.addEventListener('click', () => {
        isOpen = !isOpen;
        calc.style.left = isOpen ? '70px':'-350px';
        localStorage.setItem(STORAGE_OPEN,isOpen);
    });

    minimizeBtn.addEventListener('click', () => {
        calc.style.left = '-350px';
        isOpen = false;
        localStorage.setItem(STORAGE_OPEN,isOpen);
    });

    fullscreenBtn.addEventListener('click', () => {
        isFullscreen = !isFullscreen;
        if(isFullscreen){
            const scaleWidth = window.innerWidth;
            const scaleHeight = window.innerHeight;
            calc.style.left = '0';
            calc.style.top = '0';
            calc.style.width = scaleWidth + 'px';
            calc.style.height = scaleHeight + 'px';
        } else {
            const pos = JSON.parse(localStorage.getItem(STORAGE_POS)) || {left:70,top:50,width:320,height:'auto'};
            calc.style.left = pos.left+'px';
            calc.style.top = pos.top+'px';
            calc.style.width = pos.width+'px';
            calc.style.height = pos.height;
        }
        localStorage.setItem(STORAGE_FULLSCREEN,isFullscreen);
    });

    // Dragging
    const header = calc.querySelector('#calc-header');
    let dragging=false,offsetX=0,offsetY=0;
    header.addEventListener('mousedown',e=>{
        dragging=true;
        offsetX=e.clientX-calc.getBoundingClientRect().left;
        offsetY=e.clientY-calc.getBoundingClientRect().top;
        calc.style.transition='none';
        calc.style.cursor='grabbing';
    });
    document.addEventListener('mousemove',e=>{
        if(!dragging) return;
        e.preventDefault();
        if(!isFullscreen){
            calc.style.left=(e.clientX-offsetX)+'px';
            calc.style.top=(e.clientY-offsetY)+'px';
            calc.style.transform='none';
        }
    });
    document.addEventListener('mouseup',()=>{
        if(dragging){
            dragging=false;
            calc.style.cursor='grab';
            calc.style.transition='left 0.5s cubic-bezier(0.25,1,0.3,1)';
            localStorage.setItem(STORAGE_POS,JSON.stringify({
                left:parseInt(calc.style.left),
                top:parseInt(calc.style.top),
                width:parseInt(calc.style.width),
                height:calc.style.height
            }));
        }
    });

    // Resizer
    const resizer = document.createElement('div');
    Object.assign(resizer.style,{
        width:'10px', height:'10px', position:'absolute',
        right:0, bottom:0, cursor:'se-resize', background:'rgba(255,255,255,0.2)'
    });
    calc.appendChild(resizer);

    let resizing=false;
    resizer.addEventListener('mousedown',e=>{resizing=true;e.preventDefault();});
    document.addEventListener('mousemove',e=>{
        if(!resizing) return;
        e.preventDefault();
        calc.style.width=(e.clientX-calc.getBoundingClientRect().left)+'px';
        calc.style.height=(e.clientY-calc.getBoundingClientRect().top)+'px';
    });
    document.addEventListener('mouseup',()=>{
        if(resizing){
            resizing=false;
            localStorage.setItem(STORAGE_POS,JSON.stringify({
                left:parseInt(calc.style.left),
                top:parseInt(calc.style.top),
                width:parseInt(calc.style.width),
                height:calc.style.height
            }));
        }
    });

})();