치지직 스크린샷

영상 위에 마우스를 올릴 때만 하단 중앙에 스크린샷 버튼 표시. S 단축키 지원.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         치지직 스크린샷
// @namespace    https://your.namespace
// @version      3.5.0
// @description  영상 위에 마우스를 올릴 때만 하단 중앙에 스크린샷 버튼 표시. S 단축키 지원.
// @match        https://chzzk.naver.com/live/*
// @match        https://chzzk.naver.com/video/*
// @icon         https://i.imgur.com/vDb6wAm.png
// @run-at       document-idle
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(() => {
  'use strict';

  const Y_OFFSET_TWEAK = 0;   // 세로 위치 미세 조정 (예: -2, +2)

  GM_addStyle(`
    .chzzk-ss-fixed {
      position: fixed;
      z-index: 2147483647;
      transform: translate(-50%, -50%);
      opacity: 0;
      pointer-events: none;
      transition: opacity .18s ease;
    }
    .chzzk-ss-fixed.show { opacity: 1; }
    .chzzk-ss-btn {
      width: 44px; height: 44px;
      border: none; border-radius: 999px;
      display: inline-flex; align-items: center; justify-content: center;
      cursor: pointer; pointer-events: auto;
      background: rgba(20,20,24,0.6);
      backdrop-filter: blur(6px);
      box-shadow: 0 6px 18px rgba(0,0,0,.35);
      transition: background-color .15s, transform .05s;
    }
    .chzzk-ss-btn:hover { background: rgba(255,255,255,0.18); }
    .chzzk-ss-btn:active { transform: scale(0.96); }
    .chzzk-ss-icon { width: 22px; height: 22px; opacity: .95; pointer-events: none; filter: drop-shadow(0 1px 1px rgba(0,0,0,.28)); }
  `);

  const camURI = 'data:image/svg+xml;utf8,' + encodeURIComponent(
    `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white">
       <path d="M9 4a1 1 0 0 0-.894.553L7.382 6H5a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3V9a3 3 0 0 0-3-3h-2.382l-.724-1.447A1 1 0 0 0 15 4H9zm3 5a5 5 0 1 1 0 10 5 5 0 0 1 0-10z"/>
     </svg>`
  );

  function getMainVideo(){
    const vids=[...document.querySelectorAll('video')].filter(v=>v.videoWidth&&v.videoHeight);
    return vids.sort((a,b)=>(b.videoWidth*b.videoHeight)-(a.videoWidth*a.videoHeight))[0] || document.querySelector('video');
  }

  async function capture(){
    const v=getMainVideo(); if(!v) return;
    const c=document.createElement('canvas');
    c.width=v.videoWidth; c.height=v.videoHeight;
    c.getContext('2d').drawImage(v,0,0,c.width,c.height);
    c.toBlob(blob=>{
      const a=document.createElement('a');
      a.href=URL.createObjectURL(blob);
      a.download=`screenshot_${Date.now()}.png`;
      a.click();
      URL.revokeObjectURL(a.href);
    }, 'image/png');
  }

  let holder;
  function ensureButton(){
    if(holder && document.body.contains(holder)) return holder;
    holder = document.createElement('div');
    holder.className = 'chzzk-ss-fixed';
    const btn = document.createElement('button');
    btn.className = 'chzzk-ss-btn'; btn.title = '스크린샷 (S)';
    const img = document.createElement('img');
    img.className = 'chzzk-ss-icon'; img.src = camURI; img.alt='screenshot';
    btn.appendChild(img);
    btn.addEventListener('click', capture);
    holder.appendChild(btn);
    document.body.appendChild(holder);
    return holder;
  }

  function getControlsRect(){
    const sels = ['.pzp-pc_bottom-buttons', '.pzp-pc_bottom', '.pzp-controls', '[class*="bottom-buttons"]', '[class*="Controls"]'];
    for(const s of sels){
      const el = document.querySelector(s);
      if(!el) continue;
      const r = el.getBoundingClientRect();
      if(r.width>0 && r.height>0) return r;
    }
    return null;
  }

  function placeAtCenter(){
    const v = getMainVideo(); if(!v){ holder?.classList.remove('show'); return; }
    const vrect = v.getBoundingClientRect();
    if(vrect.width<=0 || vrect.height<=0){ holder?.classList.remove('show'); return; }

    const crect = getControlsRect();
    const x = vrect.left + vrect.width/2;
    const y = crect ? (crect.top + crect.height/2 + Y_OFFSET_TWEAK) : (vrect.bottom - 40);

    const h = ensureButton();
    h.style.left = `${x}px`;
    h.style.top  = `${y}px`;
  }

  let hoverTimer=null;
  function onMouseMove(e){
    const v=getMainVideo(); if(!v) return;
    const r=v.getBoundingClientRect();
    const inside= e.clientX>=r.left && e.clientX<=r.right && e.clientY>=r.top && e.clientY<=r.bottom;
    ensureButton();
    if(inside){
      holder.classList.add('show');
      clearTimeout(hoverTimer);
      hoverTimer=setTimeout(()=>holder.classList.remove('show'),1200);
    }
  }

  // 루프 & 이벤트
  (function loop(){ placeAtCenter(); requestAnimationFrame(loop); })();
  window.addEventListener('mousemove', onMouseMove, true);
  window.addEventListener('keydown', e=>{
    if(e.key?.toLowerCase()==='s'){ e.preventDefault(); capture(); }
  }, true);
})();