手写一个民用Tomcat (05)

继续我们这次的优化,上次是request, 这次是response,同时request,response 针对 引入门面模式,把负责的逻辑隐藏起来,展示一个 好的好的门面给servlet 。

先看下 response 

public class JxdResponse implements HttpServletResponse {
    private static final int BUFFER_SIZE = 1024;
    JxdRequest request;

    OutputStream output;
    PrintWriter writer;
    String contentType = null;
    long contentLength = -1;
    String charset = null;
    String characterEncoding = null;
    String protocol = "HTTP/1.1";

    public OutputStream getOutput() {
        return output;
    }

    public void setOutput(OutputStream output) {
        this.output = output;
    }

    public JxdResponse(OutputStream output) {
        this.output = output;
    }

    public void setRequest(JxdRequest request) {
        this.request = request;
    }

    //headers是一个保存头信息的map
    Map<String, String> headers = new ConcurrentHashMap<>();
    //默认返回OK
    String message = getStatusMessage(HttpServletResponse.SC_OK);
    int status = HttpServletResponse.SC_OK;

    @Override
    public void addHeader(String name, String value) {
        headers.put(name, value);
        if (name.toLowerCase() == DefaultHeaders.CONTENT_LENGTH_NAME) {
            setContentLength(Integer.parseInt(value));
        }
        if (name.toLowerCase() == DefaultHeaders.CONTENT_TYPE_NAME) {
            setContentType(value);
        }
    }

    @Override
    public void setHeader(String name, String value) {
        headers.put(name, value);
        if (name.toLowerCase() == DefaultHeaders.CONTENT_LENGTH_NAME) {
            setContentLength(Integer.parseInt(value));
        }
        if (name.toLowerCase() == DefaultHeaders.CONTENT_TYPE_NAME) {
            setContentType(value);
        }
    }


    //"HTTP/1.1 ${StatusCode} ${StatusName}\r\n" +
    //            "Content-Type: ${ContentType}\r\n" +
    //            "Server: minit\r\n" +
    //            "Date: ${ZonedDateTime}\r\n" +
    public void sendHeaders() throws IOException {
        PrintWriter outputWriter = getWriter();
        //下面这一端是输出状态行
        outputWriter.print(this.getProtocol());
        outputWriter.print(" ");
        outputWriter.print(status);
        if (message != null) {
            outputWriter.print(" ");
            outputWriter.print(message);
        }
        outputWriter.print("\r\n");

        if (getContentType() != null) {
            outputWriter.print("Content-Type: " + getContentType() + "\r\n");

            if (getContentLength() >= 0) {
                outputWriter.print("Content-Length: " + getContentLength() + "\r\n");
            }

            //输出头信息
            Iterator<String> names = headers.keySet().iterator();
            while (names.hasNext()) {
                String name = names.next();
                String value = headers.get(name);
                outputWriter.print(name);
                outputWriter.print(": ");
                outputWriter.print(value);
                outputWriter.print("\r\n");
            }

            //最后输出空行
            outputWriter.print("\r\n");
            outputWriter.flush();
        }
    }

    /**
     * 获取状态码返回信息
     *
     * @param status
     * @return
     */
    protected String getStatusMessage(int status) {
        switch (status) {
            case SC_OK:
                return ("OK");
            case SC_ACCEPTED:
                return ("Accepted");
            case SC_BAD_GATEWAY:
                return ("Bad Gateway");
            case SC_BAD_REQUEST:
                return ("Bad Request");
            case SC_CONTINUE:
                return ("Continue");
            case SC_FORBIDDEN:
                return ("Forbidden");
            case SC_INTERNAL_SERVER_ERROR:
                return ("Internal Server Error");
            case SC_METHOD_NOT_ALLOWED:
                return ("Method Not Allowed");
            case SC_NOT_FOUND:
                return ("Not Found");
            case SC_NOT_IMPLEMENTED:
                return ("Not Implemented");
            case SC_REQUEST_URI_TOO_LONG:
                return ("Request URI Too Long");
            case SC_SERVICE_UNAVAILABLE:
                return ("Service Unavailable");
            case SC_UNAUTHORIZED:
                return ("Unauthorized");
            default:
                return ("HTTP Response Status " + status);
        }
    }

    /**
     * 这个方法是静态方法演示
     *
     * @throws IOException
     */
    public void sendStaticResource() throws IOException {
        byte[] bytes = new byte[BUFFER_SIZE];
        FileInputStream fis = null;
        try {
            File file = new File(JxdHttpServer.FILE_ROOT, request.getUri());
            if (file.exists()) {
                fis = new FileInputStream(file);
                int ch = fis.read(bytes, 0, BUFFER_SIZE);
                while (ch != -1) {
                    output.write(bytes, 0, ch);
                    ch = fis.read(bytes, 0, BUFFER_SIZE);
                }
                output.flush();
            } else {
                String errorMessage = "HTTP/1.1 404 FIle Not Found\r\n" +
                        "Content-Type: text/html\r\n" +
                        "Content-Length: 23\r\n" +
                        "\r\n" +
                        "<h1>error未知</h1>";
                output.write(errorMessage.getBytes());
            }
        } catch (Exception e) {
            System.out.println(e.toString());
        } finally {
            if (fis != null) {
                fis.close();
            }
        }
    }

    public long getContentLength() {
        return this.contentLength;
    }

    public void setContentLength(long contentLength) {
        this.contentLength = contentLength;
    }

    public String getProtocol() {
        return protocol;
    }

    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }

    @Override
    public String getContentType() {
        return this.contentType;
    }

 

    @Override
    public PrintWriter getWriter() throws IOException {
        writer = new PrintWriter(new OutputStreamWriter(output, getCharacterEncoding()), true);
        return writer;
    }

    @Override
    public String getCharacterEncoding() {
        return characterEncoding;
    }

    @Override
    public void setCharacterEncoding(String characterEncoding) {
        this.characterEncoding = characterEncoding;
    }
}

我们整理了一下,返回引入一个getStatusMessage 方法和 sendHeaders 方法 这样不用再拼接字符串了。

在看一下我们的 JxdServletProcessor

    public void process(JxdRequest request, JxdResponse response) {
//首先根据uri最后一个/号来定位,后面的字符串认为是servlet名字
        String uri = request.getUri();
        String servletName = uri.substring(uri.lastIndexOf("/") + 1);
        URLClassLoader loader = null;
        try {
// create a URLClassLoader
            URL[] urls = new URL[1];
            URLStreamHandler streamHandler = null;
            File classPath = new File(JxdHttpServer.WEB_ROOT);
            String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString();
            urls[0] = new URL(null, repository, streamHandler);
            loader = new URLClassLoader(urls);
        } catch (IOException e) {
            System.out.println(e.toString());
        }

        //由上面的URLClassLoader加载这个servlet
        Class<?> servletClass = null;
        Servlet servlet = null;
        try {

            
            HttpRequestFacade requestFacade = new HttpRequestFacade(request);
            HttpResponseFacade responseFacade = new HttpResponseFacade(response);
            servletClass = loader.loadClass(servletName);
            response.setCharacterEncoding("UTF-8");
            response.addHeader(DefaultHeaders.CONTENT_TYPE_NAME,"text/html;charset=UTF-8");
            response.sendHeaders();

            servlet = (Servlet) servletClass.newInstance();
            servlet.service(requestFacade, responseFacade);
        } catch (ClassNotFoundException | IOException e) {
            System.out.println(e.toString());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ServletException e) {
            e.printStackTrace();
        }
    }
}

之前的 OKMessage 拼接 响应字符串方法就不需要了。

同时引入两个门面HttpRequestFacade,HttpResponseFacade

看下 我们的两个类的代码:

public class HttpRequestFacade implements HttpServletRequest {
    private HttpServletRequest request;

    public HttpRequestFacade(JxdRequest request) {
        this.request = request;
    }


    public Object getAttribute(String name) {
        return request.getAttribute(name);
    }

    public Enumeration getAttributeNames() {
        return request.getAttributeNames();
    }

    public String getCharacterEncoding() {
        return request.getCharacterEncoding();
    }

    @Override
    public void setCharacterEncoding(String s) throws UnsupportedEncodingException {
        request.setCharacterEncoding(s);
    }

    public int getContentLength() {
        return request.getContentLength();
    }

    @Override
    public long getContentLengthLong() {
        return 0;
    }

    public String getContentType() {
        return request.getContentType();
    }

    @Override
    public String getAuthType() {
        return null;
    }

    public Cookie[] getCookies() {
        return request.getCookies();
    }

    @Override
    public long getDateHeader(String s) {
        return 0;
    }

    @Override
    public String getHeader(String s) {
        return null;
    }

    public Enumeration getHeaderNames() {
        return request.getHeaderNames();
    }


    public Enumeration getHeaders(String name) {
        return request.getHeaders(name);
    }

    public ServletInputStream getInputStream() throws IOException {
        return request.getInputStream();
    }

    public int getIntHeader(String name) {
        return request.getIntHeader(name);

    }

    public String getMethod() {
        return request.getMethod();
    }

    @Override
    public String getPathInfo() {
        return null;
    }

    @Override
    public String getPathTranslated() {
        return null;
    }

    @Override
    public String getContextPath() {
        return null;
    }

    public String getParameter(String name) {
        return request.getParameter(name);
    }

    public Map getParameterMap() {
        return request.getParameterMap();
    }

    @Override
    public String getProtocol() {
        return null;
    }

    @Override
    public String getScheme() {
        return null;
    }

    @Override
    public String getServerName() {
        return null;
    }

    @Override
    public int getServerPort() {
        return 0;
    }

    public Enumeration getParameterNames() {

        return request.getParameterNames();
    }

    public String[] getParameterValues(String name) {
        return request.getParameterValues(name);
    }

    public String getQueryString() {
        return request.getQueryString();
    }

    @Override
    public String getRemoteUser() {
        return null;
    }

    @Override
    public boolean isUserInRole(String s) {
        return false;
    }

    @Override
    public Principal getUserPrincipal() {
        return null;
    }

    @Override
    public String getRequestedSessionId() {
        return null;
    }

    public BufferedReader getReader() throws IOException {
        return request.getReader();
    }

    @Override
    public String getRemoteAddr() {
        return null;
    }

    @Override
    public String getRemoteHost() {
        return null;
    }

    public String getRequestURI() {
        return request.getRequestURI();
    }

    public StringBuffer getRequestURL() {
        return request.getRequestURL();
    }

    @Override
    public String getServletPath() {
        return null;
    }

    public HttpSession getSession() {
        return request.getSession();
    }

    @Override
    public String changeSessionId() {
        return null;
    }

    @Override
    public boolean isRequestedSessionIdValid() {
        return false;
    }

    @Override
    public boolean isRequestedSessionIdFromCookie() {
        return false;
    }

    @Override
    public boolean isRequestedSessionIdFromURL() {
        return false;
    }

    @Override
    public boolean isRequestedSessionIdFromUrl() {
        return false;
    }

    @Override
    public boolean authenticate(HttpServletResponse httpServletResponse) throws IOException, ServletException {
        return false;
    }

    @Override
    public void login(String s, String s1) throws ServletException {

    }

    @Override
    public void logout() throws ServletException {

    }

    @Override
    public Collection<Part> getParts() throws IOException, ServletException {
        return null;
    }

    @Override
    public Part getPart(String s) throws IOException, ServletException {
        return null;
    }

    @Override
    public <T extends HttpUpgradeHandler> T upgrade(Class<T> aClass) throws IOException, ServletException {
        return null;
    }

    public HttpSession getSession(boolean create) {
        return request.getSession(create);
    }

    public void removeAttribute(String attribute) {
        request.removeAttribute(attribute);
    }

    @Override
    public Locale getLocale() {
        return null;
    }

    @Override
    public Enumeration<Locale> getLocales() {
        return null;
    }

    @Override
    public boolean isSecure() {
        return false;
    }

    @Override
    public RequestDispatcher getRequestDispatcher(String s) {
        return null;
    }

    @Override
    public String getRealPath(String s) {
        return null;
    }

    @Override
    public int getRemotePort() {
        return 0;
    }

    @Override
    public String getLocalName() {
        return null;
    }

    @Override
    public String getLocalAddr() {
        return null;
    }

    @Override
    public int getLocalPort() {
        return 0;
    }

    @Override
    public ServletContext getServletContext() {
        return null;
    }

    @Override
    public AsyncContext startAsync() throws IllegalStateException {
        return null;
    }

    @Override
    public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
        return null;
    }

    @Override
    public boolean isAsyncStarted() {
        return false;
    }

    @Override
    public boolean isAsyncSupported() {
        return false;
    }

    @Override
    public AsyncContext getAsyncContext() {
        return null;
    }

    @Override
    public DispatcherType getDispatcherType() {
        return null;
    }

    public void setAttribute(String key, Object value) {
        request.setAttribute(key, value);
    }
}

public class HttpResponseFacade implements HttpServletResponse {
    private HttpServletResponse response;

    public HttpResponseFacade(JxdResponse response) {
        this.response = response;
    }

    @Override
    public void addCookie(Cookie cookie) {
        response.addCookie(cookie);
    }

    @Override
    public boolean containsHeader(String s) {
        return response.containsHeader(s);
    }

    @Override
    public String encodeURL(String s) {
        return response.encodeURL(s);
    }

    @Override
    public String encodeRedirectURL(String s) {
        return response.encodeRedirectURL(s);
    }

    @Override
    public String encodeUrl(String s) {
        return response.encodeUrl(s);
    }

    @Override
    public String encodeRedirectUrl(String s) {
        return response.encodeRedirectUrl(s);
    }

    @Override
    public void sendError(int i, String s) throws IOException {
        response.sendError(i, s);
    }

    @Override
    public void sendError(int i) throws IOException {
        response.sendError(i);
    }

    @Override
    public void sendRedirect(String s) throws IOException {
        response.sendRedirect(s);
    }

    @Override
    public void setDateHeader(String s, long l) {
        response.setDateHeader(s, l);
    }

    @Override
    public void addDateHeader(String s, long l) {
        response.addDateHeader(s, l);
    }

    @Override
    public void setHeader(String s, String s1) {
        response.setHeader(s, s1);
    }

    @Override
    public void addHeader(String s, String s1) {
        response.addHeader(s, s1);
    }

    @Override
    public void setIntHeader(String s, int i) {
        response.setIntHeader(s, i);
    }

    @Override
    public void addIntHeader(String s, int i) {
        response.addIntHeader(s, i);
    }

    @Override
    public void setStatus(int i) {
        response.setStatus(i);
    }

    @Override
    public void setStatus(int i, String s) {
        response.setStatus(i, s);
    }

    @Override
    public int getStatus() {
        return response.getStatus();
    }

    @Override
    public String getHeader(String s) {
        return response.getHeader(s);
    }

    @Override
    public Collection<String> getHeaders(String s) {
        return response.getHeaders(s);
    }

    @Override
    public Collection<String> getHeaderNames() {
        return response.getHeaderNames();
    }

    @Override
    public String getCharacterEncoding() {
        return response.getCharacterEncoding();
    }

    @Override
    public String getContentType() {
        return response.getContentType();
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return response.getOutputStream();
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        return response.getWriter();
    }

    @Override
    public void setCharacterEncoding(String s) {
        response.setCharacterEncoding(s);
    }

    @Override
    public void setContentLength(int i) {
        response.setContentLength(i);
    }

    @Override
    public void setContentLengthLong(long l) {
        response.setContentLengthLong(l);
    }

    @Override
    public void setContentType(String s) {
        response.setContentType(s);
    }

    @Override
    public void setBufferSize(int i) {
        response.setBufferSize(i);
    }

    @Override
    public int getBufferSize() {
        return response.getBufferSize();
    }

    @Override
    public void flushBuffer() throws IOException {
        response.flushBuffer();
    }

    @Override
    public void resetBuffer() {
        response.resetBuffer();
    }

    @Override
    public boolean isCommitted() {
        return response.isCommitted();
    }

    @Override
    public void reset() {
        response.reset();
    }

    @Override
    public void setLocale(Locale locale) {
        response.setLocale(locale);
    }

    @Override
    public Locale getLocale() {
        return response.getLocale();
    }
}

没什么难点,主要就是引入门面模式进行整理,之前都暴露着各种方法,用户在使用的时候会看到,同时 也避免 被更改 和强转的风险。

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

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

相关文章

一篇出色的答辩状,需要在“答”与“辩”两方面下功夫,你做到了吗?

一篇出色的答辩状&#xff0c;需要在“答”与“辩”两方面下功夫&#xff0c;你做到了吗&#xff1f; 在法律诉讼中&#xff0c;答辩状的重要性不言而喻。它不仅是你回应对方指控的主要手段&#xff0c;也是展示你立场和观点的关键平台。在#李秘书讲写作#看来&#xff0c;一篇…

AMEYA360 | 纳芯微发布首款1200V SiC MOSFET

纳芯微推出1200V首款SiC MOSFET NPC060N120A系列产品&#xff0c;该产品RDSon为60mΩ&#xff0c;具有通孔式TO-247-4L与表面贴装TO-263-7L两种封装形式&#xff0c;可提供车规与工规两种等级。 纳芯微的碳化硅MOSFET具有卓越的RDSon温度稳定性、门极驱动电压覆盖度更宽、具备高…

Spring AI ETL 流水线

先纠正 Spring AI 使用本地 Ollama Embeddings 中的一个错误&#xff0c;当启动 Ollama 之后&#xff0c;Windows会有托盘图标&#xff0c;此时已经启动了 Ollama 的服务&#xff0c;访问 Embedding 时不需要运行 ollama run gemma &#xff0c;只有访问 chat 时才需要启动一个…

k-means聚类算法的MATLAB实现及可视化

K-means算法是一种无监督学习算法&#xff0c;主要用于数据聚类。其工作原理基于迭代优化&#xff0c;将数据点划分为K个集群&#xff0c;使得每个数据点都属于最近的集群&#xff0c;并且每个集群的中心&#xff08;质心&#xff09;是所有属于该集群的数据点的平均值。以下是…

中文核心计算机视觉项目分享:多通道注意力机制得农业作物图像识别检测-完整代码+论文

创新点&#xff1a; 在苹果数据集中&#xff0c;存在遮挡、不同角度的拍摄、光照变化等问题&#xff0c;导致目标检测的性能下降。为了解决这些问题&#xff0c;提出智能感知优化网络和多路径特征融合网络。 智能感知优化网络&#xff1a;帮助模型更好地关注感兴趣的目标区域&…

电商技术揭秘二十七:跨境电商物流解决方案

相关系列文章 电商技术揭秘一&#xff1a;电商架构设计与核心技术 电商技术揭秘二&#xff1a;电商平台推荐系统的实现与优化 电商技术揭秘三&#xff1a;电商平台的支付与结算系统 电商技术揭秘四&#xff1a;电商平台的物流管理系统 电商技术揭秘五&#xff1a;电商平台…

C语言学习笔记之指针(二)

指针基础知识&#xff1a;C语言学习笔记之指针&#xff08;一&#xff09;-CSDN博客 目录 字符指针 代码分析 指针数组 数组指针 函数指针 代码分析&#xff08;出自《C陷阱和缺陷》&#xff09; 函数指针数组 指向函数指针数组的指针 回调函数 qsort() 字符指针 一…

试用模方时,系统一直提示“未找到有效配置文件” ,是需要安装3dsmax吗 ?

问题如图 把文件放在认证管理服务安装目录下即可。&#xff08;注&#xff1a;因平台限制&#xff0c;需要文件的直接后台私信即可哦&#xff09; 模方是一款针对实景三维模型的冗余碎片、水面残缺、道路不平、标牌破损、纹理拉伸模糊等共性问题研发的实景三维模型修复编辑软件…

软考 - 系统架构设计师 - 数据架构真题

问题 1&#xff1a; (相当于根据题目中提到的 4 点&#xff0c;说一下关系型数据库的缺点) &#xff08;1&#xff09;.用户数量的剧增导致并发负载非常高&#xff0c;往往会达到每秒上万次读写请求。关系数据库应付每秒上万次的 SQL 查询还勉强可以&#xff0c;但是应付上万…

车载摄像头夜景增强技术解决方案,解锁高质量夜间视觉体验

随着汽车智能化的快速发展&#xff0c;车载摄像头已成为驾驶辅助系统的核心组件。尤其在夜间行驶时&#xff0c;摄像头所捕捉的画面质量直接关系到驾驶者的安全感知和行车决策。然而&#xff0c;传统的车载摄像头在夜间往往面临噪声多、画质差等挑战&#xff0c;难以满足用户对…

goland2024安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 Goland 是一款由 JetBrains 公司开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;专门用于 Go 语言的开发。它提供了丰富的功能和工具&#xff0c;帮助开发者更高效地编写、调试和管理 Go 语言项目。 功能特点&#x…

什么是Rust语言?探索安全系统编程的未来

&#x1f680; 什么是Rust语言&#xff1f;探索安全系统编程的未来 文章目录 &#x1f680; 什么是Rust语言&#xff1f;探索安全系统编程的未来摘要引言正文&#x1f4d8; Rust语言简介&#x1f31f; 发展历程&#x1f3af; Rust的技术意义和优势&#x1f4e6; Rust解决的问题…

基于逐笔数据合成高频订单簿:DolphinDB 订单簿引擎

订单簿是交易市场上买卖双方正在报价的不同价格的列表。订单簿快照反应了特定时刻市场上的交易意图&#xff0c;比如交易活跃的证券标的往往有着密集的订单簿。订单簿快照对量化金融的交易策略、风险管理和市场分析等方面都具有重要意义。 通常交易所可以提供实时和历史的行情…

无界系统实战课:全体系落地无界改版后选择、出价、高投产做付费引流-38节

课程内容&#xff1a; 001.01、如何快速学习无界推广(新学员先听).mp4 002.02、如何快速上手和适应无界(老学员先听).mp4 003.03、无界推广在运营中的作用(必听).mp4 004.04、无界多工具如何选择(必听).mp4 005.05、自定义出价、控成本、最大化底层逻辑和选择(必听).mp4 …

postgres插件部署+函数开发 - pl/java安装(centos7)

一、安装postgres sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo yum install -y postgresql11-server sudo /usr/pgsql-11/bin/postgresql-11-setup initdb sudo systemctl enable postg…

stable diffusion--小白学习步骤

1.看一下Unet网络的讲解_哔哩哔哩_bilibili&#xff0c;了解Unet网络 2.看一下【生成式AI】Diffusion Model 原理剖析 (1/4)_哔哩哔哩_bilibili&#xff0c;起码要看前3/6个视频 3.看一下超详细的扩散模型&#xff08;Diffusion Models&#xff09;原理代码 - 知乎 (zhihu.co…

前端-vue项目debugger调试

一、前言 有的时候接受同事一个项目&#xff0c;用框架不一样&#xff0c;写的也不太规范&#xff0c;那么就需要打断点去学习改项目的流程了。 那么vue项目是如何debugger调试呢&#xff1f; 二、操作 大概理解一下&#xff0c;vue项目启动&#xff0c;大概是先启动框架&am…

nginx 卸载和安装超详细教程

一、前言 由于现在nginx有版本漏洞&#xff0c;所以很多安装过nginx的需要卸载重新安装&#xff0c;没安装过的&#xff0c;切记不要乱安装版本。 OK以上版本切记不能再用了&#xff01; 废话不多说&#xff0c;直接上干货。 二、卸载 1、停止Nginx进程 命令行停止&#xf…

【C++成长记】C++入门 | 类和对象(上) |面向过程和面向对象初步认识、类的引入、类的定义、类的访问限定符及封装

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;C❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 一、面向过程和面向对象初步认识 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步…

【日常记录】【CSS】利用动画延迟实现复杂动画

文章目录 1、介绍2、原理3、代码4、参考链接 1、介绍 对于这个效果而言&#xff0c;最先想到的就是 监听滑块的input事件来做一些操作 ,但是会发现&#xff0c;对于某一个节点的时候&#xff0c;这个样式操作起来比较麻烦 只看这个代码的话&#xff0c;发现他用的是动画&#x…
最新文章