两张图片对比clip功能

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>图片拖动Clip对比功能</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {font-family: Arial, sans-serif;background-color: #f0f0f0;display: flex;justify-content: center;align-items: center;min-height: 100vh;padding: 20px;}.container {max-width: 800px;width: 100%;height: 500px;border-radius: 12px;overflow: hidden;background-color: white;box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);border: 1px solid #e0e0e0;}.image-comparison {position: relative;height: 100%;width: 100%;display: flex;overflow: hidden;touch-action: none;user-select: none;}.image-comparison img {display: block;width: 100%;height: 100%;object-fit: cover;object-position: center center;}.clip-item {position: absolute;top: 0;left: 0;width: 100%;height: 100%;user-select: none;will-change: clip-path;clip-path: inset(0 0 0 50%);transition: clip-path 0.1s ease-out;}.handle-container {position: absolute;top: 0;height: 100%;background: none;border: 0;padding: 0;pointer-events: all;appearance: none;outline: 0;transform: translate3d(-50%, 0, 0);left: 50%;cursor: ew-resize;z-index: 10;}.handle-root {display: flex;flex-direction: column;place-items: center;height: 100%;cursor: ew-resize;pointer-events: none;color: white;}.handle-line {flex-grow: 1;height: 100%;width: 2px;background-color: currentColor;pointer-events: auto;box-shadow: 0 0 4px rgba(0, 0, 0, 0.5);}.handle-button {display: grid;grid-auto-flow: column;gap: 8px;place-content: center;flex-shrink: 0;width: 56px;height: 56px;border-radius: 50%;border: 2px solid currentColor;pointer-events: auto;backdrop-filter: blur(7px);background-color: rgba(0, 0, 0, 0.125);box-shadow: 0 0 4px rgba(0, 0, 0, 0.35);}.handle-arrow {width: 0;height: 0;border-top: 8px solid transparent;border-right: 10px solid currentColor;border-bottom: 8px solid transparent;}.handle-arrow:last-child {transform: rotate(180deg);}.info {position: absolute;bottom: 20px;left: 20px;background: rgba(0, 0, 0, 0.7);color: white;padding: 10px 15px;border-radius: 8px;font-size: 14px;z-index: 5;}@media (max-width: 768px) {.container {height: 400px;}.handle-button {width: 48px;height: 48px;}}</style>
</head>
<body><div class="container"><div class="image-comparison" id="imageComparison"><img alt="Original Image" src="./img/demo5.png"><div class="clip-item" id="clipItem"><img alt="Translated Image" src="./img/demo6.png"></div><button class="handle-container" id="handleContainer" aria-label="拖动移动或聚焦并使用箭头键" aria-orientation="horizontal" aria-valuemin="0" aria-valuemax="100" aria-valuenow="50" role="slider"><div class="handle-root"><div class="handle-line"></div><div class="handle-button"><div class="handle-arrow"></div><div class="handle-arrow"></div></div><div class="handle-line"></div></div></button><div class="info">拖动中间的滑块来对比两张图片</div></div></div><script>class ImageComparison {constructor(container) {this.container = container;this.clipItem = container.querySelector('#clipItem');this.handleContainer = container.querySelector('#handleContainer');this.isDragging = false;this.currentPosition = 50; // 初始位置 50%this.init();}init() {this.bindEvents();this.updatePosition(this.currentPosition);}bindEvents() {// 鼠标事件this.handleContainer.addEventListener('mousedown', this.handleMouseDown.bind(this));document.addEventListener('mousemove', this.handleMouseMove.bind(this));document.addEventListener('mouseup', this.handleMouseUp.bind(this));// 触摸事件this.handleContainer.addEventListener('touchstart', this.handleTouchStart.bind(this));document.addEventListener('touchmove', this.handleTouchMove.bind(this));document.addEventListener('touchend', this.handleTouchEnd.bind(this));// 键盘事件this.handleContainer.addEventListener('keydown', this.handleKeyDown.bind(this));// 防止拖动时选中文本this.container.addEventListener('selectstart', (e) => e.preventDefault());}handleMouseDown(e) {e.preventDefault();this.isDragging = true;this.container.style.cursor = 'ew-resize';this.clipItem.style.transition = 'none';}handleMouseMove(e) {if (!this.isDragging) return;e.preventDefault();const rect = this.container.getBoundingClientRect();const x = e.clientX - rect.left;const percentage = (x / rect.width) * 100;this.updatePosition(Math.max(0, Math.min(100, percentage)));}handleMouseUp() {this.isDragging = false;this.container.style.cursor = '';this.clipItem.style.transition = 'clip-path 0.1s ease-out';}handleTouchStart(e) {e.preventDefault();this.isDragging = true;this.clipItem.style.transition = 'none';}handleTouchMove(e) {if (!this.isDragging) return;e.preventDefault();const touch = e.touches[0];const rect = this.container.getBoundingClientRect();const x = touch.clientX - rect.left;const percentage = (x / rect.width) * 100;this.updatePosition(Math.max(0, Math.min(100, percentage)));}handleTouchEnd() {this.isDragging = false;this.clipItem.style.transition = 'clip-path 0.1s ease-out';}handleKeyDown(e) {const step = 5;switch(e.key) {case 'ArrowLeft':e.preventDefault();this.updatePosition(Math.max(0, this.currentPosition - step));break;case 'ArrowRight':e.preventDefault();this.updatePosition(Math.min(100, this.currentPosition + step));break;case 'Home':e.preventDefault();this.updatePosition(0);break;case 'End':e.preventDefault();this.updatePosition(100);break;}}updatePosition(percentage) {this.currentPosition = percentage;// 更新clip-paththis.clipItem.style.clipPath = `inset(0 0 0 ${percentage}%)`;// 更新滑块位置this.handleContainer.style.left = `${percentage}%`;// 更新ARIA属性this.handleContainer.setAttribute('aria-valuenow', Math.round(percentage));}}// 初始化document.addEventListener('DOMContentLoaded', () => {const container = document.getElementById('imageComparison');new ImageComparison(container);});</script>
</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/561.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据管理新范式:基于Docker的私有云存储系统构建指南

文章目录前言1. 创建NextCloud容器2. 公网远程访问本地NextCloud容器2.1 内网穿透工具安装3.2 创建远程连接公网地址3. 固定NextCloud私有云盘公网地址前言 在数字资产价值日益凸显的今天&#xff0c;构建自主可控的存储体系已成为技术从业者的核心诉求。本文将深入解析如何通…

vue3官方文档学习心得

这几天抽空把vue3的文档整个看了一遍。简介 | Vue.js 23年写过一个vue2的项目&#xff0c;24年写了一个vue3的项目&#xff0c;页面功能比较简单&#xff0c;用几个简单的API&#xff0c;watch、watchEffect、ref、reactive就能实现的业务功能。 写了几年的react的&#xff0…

蓝桥杯 第十六届(2025)真题思路复盘解析

本文以洛谷平台所提供的题目描述及评测数据为基础进行讲解。 前言&#xff1a;这是本人的蓝桥杯试卷&#xff0c;大概排省一前40%的位置&#xff0c;实际上这届题目偏难&#xff0c;我没有做出太多的有效得分。我把当时的思路和现在学习的思路都复盘进来&#xff0c;希望给大家…

使用python的 FastApi框架开发图书管理系统-前后端分离项目分享

今天给大家分享一个 我最近使用python 框架 fastapi 写的一个web项目 &#xff0c;叫图书管理系统。项目主要是来巩固 python的编程技术。使用的是前端后 分离开发。 主要实现的功能&#xff1a; 1、用户管理&#xff1a;可以新增、编辑、删除用户信息。 2、图书管理&#xff1…

Redis-哨兵机制Sentinel

redis的主从复制模式下,一旦主节点出现了故障无法提供服务了,需要人工进行主从切换,同时大量的客户端需要被通知切换到新的主节点上,对于有了一定规模的应用来说,这种方案的延迟是无法接受的,于是redis2.8提供了Redis-Sentinel(哨兵)来解决这个问题. 目录 1.啥是哨兵节点: 2.r…

Java 阻塞队列:7种类型全解析

在 Java 中&#xff0c;阻塞队列&#xff08;BlockingQueue&#xff09; 是一种线程安全的队列结构&#xff0c;用于实现生产者-消费者模式。它的核心特性是在队列为空时阻塞消费者线程&#xff0c;在队列满时阻塞生产者线程&#xff0c;从而自动协调线程之间的协作。阻塞队列的…

word中的单位详解

Word中的单位转换全面指南 一、Word中支持的单位类型及转换关系 1. 常用单位类型 磅&#xff08;pt&#xff09;&#xff1a;国际通用排版单位&#xff0c;1磅 ≈ 0.03527厘米&#xff0c;1厘米 ≈ 28.35磅。厘米&#xff08;cm&#xff09;&#xff1a;公制单位&#xff0c;1厘…

stm32--SPI原理应用W25Q64(二)

目录 一 概述 二 W25Q64的介绍 简介 硬件样子 主要特性 常用 SPI 指令 三 代码部分 前言 SPI.c 新加代码第一部分&#xff1a; 新加代码第二部分&#xff1a; 新加代码第三部分&#xff1a; 新加代码第四部分&#xff1a; 新加代码第五部分&#xff08;读&#xf…

关于 c、c#、c++ 三者区别

1. 起源与定位 语言起源时间开发者定位/特点C1972年Dennis Ritchie面向过程的编程语言&#xff0c;强调底层控制与高效性能C1983年Bjarne Stroustrup在 C 的基础上加入 面向对象编程&#xff08;OOP&#xff09;C#2000年微软&#xff08;Microsoft&#xff09;类似 Java&#…

7.7晚自习作业

实操作业02&#xff1a;Spark核心开发 作业说明 请严格按照步骤操作&#xff0c;并将最终结果文件&#xff08;命名为&#xff1a;sparkcore_result.txt&#xff09;于20点前上传。结果文件需包含每一步的关键命令执行结果文本输出。 一、数据读取与转换操作 上传账户数据$…

剑指offer第2版:动态规划+记忆化搜索

前三题是同一种模型&#xff0c;所以我分别用递推、记忆化、动归来做 一、p74-JZ10 斐波那契数列 斐波那契数列_牛客题霸_牛客网 class Solution { public:int Fibonacci(int n) {// write code hereif(n1||n2) return 1;int a1,b1,c1;while(n>2){cab;ab;bc;--n;}return c…

爬虫的笔记整理

网络爬虫首先要认识http和https协议 在浏览器中发送一个http请求&#xff1a; 1.输入一个URL地址之后&#xff0c;向http服务器发送请求&#xff0c;主要分为GET和POST两种方法 2.输入URL之后&#xff0c;发送一个request请求&#xff0c;这时候服务器把response文件对象发送…