HTML - 简易版打字练习

news2024/9/25 15:23:11

1. 赛博朋克风格的视觉设计

  • 颜色与渐变:通过linear-gradient设置了背景的颜色渐变,使用高饱和度的霓虹色彩(如橙色、绿色和蓝色)来营造赛博朋克的视觉效果。这种配色方案是赛博朋克风格的典型元素。

  • 立体感和阴影:使用 box-shadow 为字符方框添加阴影,使其看起来具有一定的立体感和浮动感,模拟电子设备或键盘按键的效果。

  • 文本阴影:通过 text-shadow 给字符添加阴影,增强了赛博朋克风格的霓虹灯效果。这种效果在高对比度的背景下尤其突出,营造出虚拟世界的视觉效果。

2. 动态效果与动画

  • Glitch动画:利用 @keyframes 定义了 glitch 动画,通过clip-pathtransform模拟文本的抖动和错位,营造出电子干扰(glitch)的效果。这种故障效果是赛博朋克风格中常见的表现形式,模拟了数字世界中不稳定的电子信号。

  • 伪元素 ::after:使用 ::after 伪元素在每个字符方框后叠加一个内容相同的元素,通过visibility控制显示与隐藏,并在鼠标悬停时触发 glitch 动画,使其看起来像是字符发生了瞬间故障。

3. 交互与响应

  • 鼠标悬停效果:在 .character-box:hover::after 中定义了鼠标悬停时的动画效果,当用户将鼠标悬停在字符方框上时,伪元素 ::after 显示并触发 glitch 效果。这种交互为页面增添了动态元素,使用户的体验更加生动。

  • 按键状态变化:通过CSS类的切换(如 .correct, .incorrect, .highlighted)动态更新字符方框的状态和颜色,实时反馈用户输入的正确性。这种视觉反馈让用户能够迅速了解自己输入的正确与否。

4. 布局与排版

  • 容器布局:使用 display: inline-block;text-align: center; 将字符方框、输入框和结果展示区域合理布局。整个页面通过设置 widthmargin,保持在不同设备和屏幕尺寸上的一致性。

  • 字符方框的设计:每个字符被放置在独立的 .character-box 容器中,使得每个字符都有自己的背景、阴影和动画效果。这种设计不仅清晰美观,还增强了赛博朋克风格的整体感。

5. JavaScript 动态逻辑

  • 分页显示:通过JavaScript将长文本拆分为每页100个字符,并在用户打完一页后自动切换到下一页,实现了文本的分页显示,防止内容过于拥挤。

  • 实时输入检查:JavaScript动态检查用户输入的每个字符,利用 .correct, .incorrect, .highlighted 类名的切换实现实时的视觉反馈。

主要代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>中文打字练习</title>
    <link href="https://fonts.font.im/css?family=Do+Hyeon" rel="stylesheet">
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            margin-top: 50px;
            background-image: url('https://bkimg.cdn.bcebos.com/pic/0ff41bd5ad6eddc451da707ff483a1fd5266d11695a4?x-bce-process=image/format,f_auto/quality,Q_70/resize,m_lfit,limit_1,w_536');
            background-size: cover;
            background-position: center;
            background-attachment: fixed;
            color: white;
        }
        #text-to-type-container {
            display: inline-block;
            font-size: 24px;
            margin: 20px 0;
            background-color: rgba(0, 0, 0, 0.5);
            padding: 10px;
            border-radius: 10px;
            text-align: left;
            width: 80%;
        }
        .character-box {
            display: inline-block;
            width: 40px;
            height: 55px;
            line-height: 55px;
            text-align: center;
            margin: 2px;
            font-weight: bold;
            font-family: 'Do Hyeon', sans-serif;
            border-radius: 5px;
            background: linear-gradient(30deg,transparent 10%,rgb(255, 136, 0) 10% 95%,  rgb(0, 255, 149) 95%);
            box-shadow: 5px 0 0 rgb(0, 204, 255);
            color: rgb(255, 251, 251);
            position: relative;
            overflow: hidden;
        }
        .character-box::after {
            content: attr(data-char);
            position: absolute;
            top: 0;
            left: 0;
            text-shadow: -5px -2px 0 rgb(0, 183, 255),
            5px 2px 0 rgb(0, 255, 115);
            visibility: hidden;
            width: 100%;
            height: 100%;
            background: linear-gradient(30deg,transparent 10%,rgb(255, 136, 0) 10% 95%,  rgb(0, 255, 149) 95%);
        }
        .character-box.correct {
            background: linear-gradient(30deg,transparent 10%,#34a853 10% 95%, #a8e6cf 95%);
            box-shadow: 5px 0 0 #34a853;
        }
        .character-box.incorrect {
            background: linear-gradient(30deg,transparent 10%,#d32f2f 10% 95%, #ff8a80 95%);
            box-shadow: 5px 0 0 #d32f2f;
        }
        .character-box.highlighted {
            background: linear-gradient(30deg,transparent 10%,#fbc02d 10% 95%, #fff176 95%);
            box-shadow: 5px 0 0 #fbc02d;
        }
        .character-box:hover::after {
            animation: glitch 1s;
            animation-timing-function: steps(1, end);
            visibility: visible;
        }
        @keyframes glitch {
            0% {
                clip-path: inset(20% -5px 60% 0);
                transform: translate(-6px, 5px);
            }
            10% {
                clip-path: inset(50% -5px 30% 0);
                transform: translate(6px, -5px);
            }
            20% {
                clip-path: inset(20% -5px 60% 0);
                transform: translate(5px, 0px);
            }
            30% {
                clip-path: inset(80% -5px 5% 0);
                transform: translate(-8px, 5px);
            }
            40% {
                clip-path: inset(0 -5px 80% 0);
                transform: translate(-4px, -3px);
            }
            50% {
                clip-path: inset(50% -5px 30% 0);
                transform: translate(-6px, -5px);
            }
            60% {
                clip-path: inset(80% -5px 5% 0);
                transform: translate(-7px, 5px);
            }
            70% {
                clip-path: inset(0 -5px 80% 0);
                transform: translate(3px, 6px);
            }
            80% {
                clip-path: inset(50% -5px 30% 0);
                transform: translate(5px, 5px);
            }
            90% {
                clip-path: inset(20% -5px 60% 0);
                transform: translate(6px, -5px);
            }
            100% {
                clip-path: inset(0 -5px 80% 0);
                transform: translate(1px, 5px);
            }
        }
        #user-input {
            width: 80%;
            height: 100px;
            font-size: 24px;
            margin-top: 20px;
            border: 2px solid #ccc;
            padding: 10px;
            outline: none;
            background-color: rgba(255, 255, 255, 0.8);
            border-radius: 10px;
            color: black;
        }
        #results {
            margin-top: 20px;
            background-color: rgba(0, 0, 0, 0.5);
            padding: 10px;
            border-radius: 10px;
            display: inline-block;
        }
        #pagination {
            margin-top: 20px;
        }
        button {
            font-size: 18px;
            padding: 10px 20px;
            border: none;
            border-radius: 5px;
            background-color: #007bff;
            color: white;
            cursor: pointer;
            box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
            transition: background-color 0.3s ease;
        }
        button:hover {
            background-color: #0056b3;
        }
        button:disabled {
            background-color: #cccccc;
            cursor: not-allowed;
        }
    </style>
</head>
<body>
    <h1>中文打字练习</h1>
    <div id="text-to-type-container"></div>
    <textarea id="user-input" placeholder="在此输入..." oninput="checkTyping()" onblur="checkCompletion()"></textarea>
    <div id="results">
        <p>打字速度: <span id="speed">0</span> 字/分钟</p>
        <p>准确率: <span id="accuracy">100</span>%</p>
        <p>用时: <span id="time-taken">0</span> 秒</p>
    </div>
    <div id="pagination">
        <button onclick="previousPage()" disabled id="prev-button">上一页</button>
        <button onclick="nextPage()" id="next-button">下一页</button>
    </div>

    <script>
        const textToType = "王楚钦,男,2000年5月11日出生于吉林省吉林市,国际级运动健将 ,中国男子乒乓球运动员。效力于山东魏桥乒乓球俱乐部和中国男子乒乓球队。 2015年12月,升入中国国家乒乓球队一队。2017年12月与薛飞获2017世界青少年锦标赛男双冠军 。2018年7月获2018年韩国乒乓球公开赛混双亚军;8月获2018年雅加达亚运会乒乓球男团冠军 ;10月获得2018布宜诺斯艾利斯青奥会乒乓球男单冠军 。2019年12月9日获“北京青年榜样·时代楷模”人物评选“青少年体育之星”。2021年7月入选2020年东京奥运会中国体育代表团乒乓球项目运动员名单;9月获第十四届全运会男双冠军;2021年11月休斯顿世乒赛混双搭档孙颖莎夺得混双金牌 。2022年1月获WTT澳门冠军赛男子单打冠军;10月获成都第56届世界乒乓球团体锦标赛冠军、WTT澳门冠军赛男子单打冠军 、新乡WTT世界杯男子单打冠军 。2023年4月获2023年WTT冠军赛澳门站男单冠军;9月获杭州第19届亚运会乒乓球男子团体、混双、男单、男双冠军。2024年2月获釜山世乒赛团体赛男子团体决赛冠军 ;5月获2024年WTT沙特阿拉伯大满贯男单、男双、混双冠军 。2024年巴黎奥运会,王楚钦入选中国国家乒乓球队大名单,出战男单、男团以及混双项目。2024年7月获得巴黎奥运会乒乓球混双冠军。";
        const textToTypeContainer = document.getElementById('text-to-type-container');
        const userInput = document.getElementById('user-input');
        const speedDisplay = document.getElementById('speed');
        const accuracyDisplay = document.getElementById('accuracy');
        const timeTakenDisplay = document.getElementById('time-taken');
        const prevButton = document.getElementById('prev-button');
        const nextButton = document.getElementById('next-button');

        const charsPerPage = 100;
        let currentPage = 0;
        let totalPages = Math.ceil(textToType.length / charsPerPage);

        let startTime = null;
        let endTime = null;
        let typedCharacters = 0;

        function displayText() {
            textToTypeContainer.innerHTML = '';
            const start = currentPage * charsPerPage;
            const end = Math.min(start + charsPerPage, textToType.length);
            const pageText = textToType.slice(start, end);

            pageText.split('').forEach(char => {
                const span = document.createElement('span');
                span.innerText = char;
                span.classList.add('character-box');
                span.setAttribute('data-char', char);
                textToTypeContainer.appendChild(span);
            });
        }

        displayText();

        function checkTyping() {
            const typedText = userInput.value;
            if (!startTime) {
                startTime = new Date();
            }

            typedCharacters = typedText.length;

            // 计算打字速度 (字/分钟)
            const elapsedTime = (new Date() - startTime) / 60000; // 转换为分钟
            const speed = Math.round(typedCharacters / elapsedTime);
            speedDisplay.innerText = speed;

            // 计算准确率
            let correctCharacters = 0;
            const characters = textToTypeContainer.children;

            for (let i = 0; i < characters.length; i++) {
                const currentChar = characters[i];

                if (i < typedText.length) {
                    if (typedText[i] === currentChar.innerText) {
                        currentChar.classList.add('correct');
                        currentChar.classList.remove('incorrect', 'highlighted');
                        correctCharacters++;
                    } else {
                        currentChar.classList.add('incorrect');
                        currentChar.classList.remove('correct', 'highlighted');
                    }
                } else if (i < typedCharacters) {
                    currentChar.classList.add('highlighted');
                    currentChar.classList.remove('correct', 'incorrect');
                } else {
                    currentChar.classList.remove('correct', 'incorrect', 'highlighted');
                }
            }

            const accuracy = Math.round((correctCharacters / typedCharacters) * 100);
            accuracyDisplay.innerText = isNaN(accuracy) ? 100 : accuracy;

            // 检查是否完成当前页
            if (typedText.length >= characters.length && currentPage < totalPages - 1) {
                userInput.value = ''; // 清空输入框
                nextPage();
            } else if (typedText.length >= characters.length && currentPage === totalPages - 1) {
                endTime = new Date();
                const totalTimeTaken = ((endTime - startTime) / 1000).toFixed(2); // 以秒为单位
                timeTakenDisplay.innerText = totalTimeTaken;
            }
        }

        function nextPage() {
            if (currentPage < totalPages - 1) {
                currentPage++;
                displayText();
                prevButton.disabled = false;
                if (currentPage === totalPages - 1) {
                    nextButton.disabled = true;
                }
            }
        }

        function previousPage() {
            if (currentPage > 0) {
                currentPage--;
                displayText();
                nextButton.disabled = false;
                if (currentPage === 0) {
                    prevButton.disabled = true;
                }
            }
        }
    </script>
</body>
</html>

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

春秋云镜CVE-2023-38836

打开靶场环境 点击发现一个登陆框&#xff0c;弱口令试一下 发现账号密码为admin,password 随便点击点击 Media发现这里可以上传文件上传木马试试 <?php eval($_POST["wjq"]); ?> 发现不能上传php文件 php内容 修改他的格式 抓包绕过一下 302就可以其实已经…

绘制echarts-liquidfill水球图

文章目录 一、效果图二、步骤1.安装插件2.引入2.主要代码2.素材图片 总结 一、效果图 二、步骤 1.安装插件 npm install echarts npm install echarts-liquidfillecharts5的版本与echarts-liquidfill3兼容&#xff0c;echarts4的版本与echarts-liquidfill2兼容,安装的时候需要…

Spring I

1. IoC & DI ⼊⻔ 1.1 Spring 是什么&#xff1f; 通过前⾯的学习, 我们知道了Spring是⼀个开源框架, 他让我们的开发更加简单. 他⽀持⼴泛的应⽤场 景, 有着活跃⽽庞⼤的社区, 这也是Spring能够⻓久不衰的原因. 但是这个概念相对来说, 还是⽐较抽象. 我们⽤⼀句更具体的…

C++中的搜索二叉树模拟实现代码

包含的功能&#xff1a;插入、删除、查找、中序遍历&#xff08;排列大小&#xff09; 代码&#xff1a;

Linux 内核源码分析---proc 文件系统

proc文件系统 进程数据文件系统&#xff08;process data filesystem, procfs&#xff09;装载在 /proc&#xff0c;缩写为 procFS。 proc 文件系统是一种虚拟文件系统&#xff0c;其信息不能从块设备读取。只有在读取文件内容时才动态生成相应的信息。使用proc文件系统&…

WPF篇(7)- Control基类+ContentControl类(内容控件)+ButtonBase基类

前面给大家介绍完了WPF所有的布局控件&#xff0c;属性以及使用案例&#xff0c;从这里咱们就开始学下内容控件。 Control基类 Control是许多控件的基类。比如最常见的按钮&#xff08;Button&#xff09;、单选(RadioButton)、复选&#xff08;CheckBox&#xff09;、文本框…

【JavaEE初阶】JUC(java.uitl.concurrent)的常见类

&#x1f334;Callable 接口 之前讲到Runnable接口&#xff0c;用于描述一个任务&#xff0c;通过Runnable中的run方法来体现的&#xff0c;但是描述的任务没有返回值&#xff08;run方法是返回void&#xff09;&#xff0c;此处的Callable接口与Runnable接口类似&#xff0c;C…

LLVM理论篇之编译器前端

1、简述 LLVM前端通过对源程序的预处理&#xff0c;构成源程序的字符流扫描与分解&#xff0c;将单词序列提取为各类语法短语&#xff0c;生成抽象语法树&#xff0c;最终转换为中间代码。编译器前端包含的这几个过程如下&#xff1a; &#xff08;1&#xff09;预编译 &…

【python】实现对文件夹中的图像连续重命名方法

import os import shutildef rename_images(input_folder):# 获取输入文件夹下的所有图片文件&#xff08;假设都是.jpg格式&#xff09;image_files [f for f in os.listdir(input_folder) if os.path.isfile(os.path.join(input_folder, f)) and f.endswith(".jpg"…

性能测试工具LoadRunner

前言&#x1f440;~ 上一章我们介绍了性能测试的一些基本概念&#xff0c;重要的是性能测试的各项指标&#xff0c;今天我们使用性能测试工具LoadRunner简单的完成一次性能测试 性能测试Load Runner LoadRunner是什么&#xff1f; LoadRunner安装 LoadRunner脚本录制 1.录…

振弦式钢筋计的主要应用领域有哪些?

在现代工程建设中&#xff0c;钢筋作为重要的结构材料&#xff0c;其应力状态直接关系到工程结构的安全性和稳定性。因此&#xff0c;对钢筋应力的实时监测成为了保障工程质量的关键环节。振弦式钢筋计作为一种高精度、稳定性好的应力监测仪器&#xff0c;在桥梁、建筑、隧道等…

pg一些常用语句记录

查看数据库大小 pg_size_pretty:将数据库用量展示为KB、MB、GB等样式,查看更直观查看具体某个数据库的大小 select pg_size_pretty(pg_database_size(postgres)); 查看所有数据库的大小 select pg_database.datname, pg_size_pretty (pg_database_size(pg_database.datnam…

Sqli-labs-master靶场--布尔盲注

目录 1、布尔盲注 2、布尔盲注的流程&#xff08;以靶场less-8为例&#xff09; 2.1输入id尝试是否存在注入点 2.1.1通过以上尝试&#xff0c;联想到可能是布尔盲注 2.2猜测数据库长度 2.3获取数据库名 2.3.1python脚本获取 代码&#xff1a; 获取结果为&#xff1a; …

后期调色学习笔记

关于调色曲线的学习&#xff1a; 学习链接&#xff1a;一看就懂的曲线调色教程【手机摄影后期】_哔哩哔哩_bilibili 从左向右就是由暗部越来越到亮部 越靠近右侧的越是亮部

机械学习—零基础学习日志(数学基础汇总1)

零基础为了学人工智能&#xff0c;正在艰苦的学习 我比较推荐&#xff0c;《三个月从零入门深度学习&#xff0c;保姆级学习路线图》的整体学习思路&#xff0c;但因为数学基础太差&#xff0c;而且针对所需的数学系统知识&#xff0c;我依然没有很明确的学习方向。 所以直接…

杀手级的AI LLM产品还有多久面世

AI产品落地&#xff1a;Killer App依然缺席的原因与未来展望 &#x1f680; 在当今科技领域&#xff0c;人工智能&#xff08;AI&#xff09;无疑正处于一个快速发展的阶段。尽管如此&#xff0c;关于AI产品的实际应用和市场落地&#xff0c;仍然存在诸多挑战。本文将探讨当前…

使用 Matplotlib 绘制折线图

使用 Matplotlib 绘制折线图 数据可视化是数据分析的重要组成部分&#xff0c;通过图表&#xff0c;我们可以更直观地理解数据背后的趋势和模式。Matplotlib 是 Python 最基础也是最常用的绘图库之一&#xff0c;非常适合初学者。本文将带你从零开始&#xff0c;逐步创建和自定…

Python Static Typing: 提升代码可靠性与可读性的使用技巧

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storm…

Java毕业设计 基于SSM和Vue的酒店管理系统小程序

Java毕业设计 基于SSM和Vue的酒店管理系统小程序 这篇博文将介绍一个基于SSM框架和Vue开发的酒店管理系统微信小程序&#xff0c;适合用于Java毕业设计。 功能介绍 用户 登录 注册 忘记密码 首页 图片轮播 房间信息 房间详情 预订 收藏 评论 我的 订单信息 酒店管理…

Python 常用内置函数

目录 1、enumerate函数 1.1、for循环中使用 1.2、enumerate指定索引的起始值 1.3、enumerate在线程中的作用 2、Map 函数 2.1、map()函数可以传多个迭代器对象 3、lambda表达式&#xff08;匿名函数&#xff09; 示例 4、sort函数和sorted函数 4.1、sort()函数 4.2、…