工程化与框架系列(26)--前端可视化开发

前端可视化开发 📊

引言

前端可视化是现代Web应用中不可或缺的一部分,它能够以直观的方式展示复杂的数据和信息。本文将深入探讨前端可视化开发的关键技术和最佳实践,包括图表绘制、数据处理、动画效果等方面。

可视化技术概述

前端可视化主要包括以下技术方向:

  • Canvas绘图:像素级别的图形绘制
  • SVG矢量图:可缩放的矢量图形
  • WebGL 3D:三维图形渲染
  • 可视化库:ECharts、D3.js等
  • 地理信息:地图可视化

Canvas图形绘制

基础绘图API

// Canvas绘图管理器
class CanvasRenderer {
    private canvas: HTMLCanvasElement;
    private ctx: CanvasRenderingContext2D;
    
    constructor(canvas: HTMLCanvasElement) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d')!;
        
        this.initializeCanvas();
    }
    
    // 初始化画布
    private initializeCanvas(): void {
        // 设置画布尺寸为显示尺寸的2倍,提高清晰度
        const displayWidth = this.canvas.clientWidth;
        const displayHeight = this.canvas.clientHeight;
        
        this.canvas.width = displayWidth * 2;
        this.canvas.height = displayHeight * 2;
        
        // 缩放上下文以匹配显示尺寸
        this.ctx.scale(2, 2);
        
        // 设置默认样式
        this.ctx.lineWidth = 2;
        this.ctx.strokeStyle = '#333';
        this.ctx.fillStyle = '#666';
    }
    
    // 清空画布
    clear(): void {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    }
    
    // 绘制直线
    drawLine(
        startX: number,
        startY: number,
        endX: number,
        endY: number,
        options: LineOptions = {}
    ): void {
        this.ctx.save();
        
        // 应用样式选项
        if (options.color) this.ctx.strokeStyle = options.color;
        if (options.width) this.ctx.lineWidth = options.width;
        if (options.dash) this.ctx.setLineDash(options.dash);
        
        // 绘制路径
        this.ctx.beginPath();
        this.ctx.moveTo(startX, startY);
        this.ctx.lineTo(endX, endY);
        this.ctx.stroke();
        
        this.ctx.restore();
    }
    
    // 绘制矩形
    drawRect(
        x: number,
        y: number,
        width: number,
        height: number,
        options: ShapeOptions = {}
    ): void {
        this.ctx.save();
        
        // 应用样式选项
        if (options.fillColor) this.ctx.fillStyle = options.fillColor;
        if (options.strokeColor) this.ctx.strokeStyle = options.strokeColor;
        if (options.lineWidth) this.ctx.lineWidth = options.lineWidth;
        
        // 绘制矩形
        if (options.fillColor) {
            this.ctx.fillRect(x, y, width, height);
        }
        if (options.strokeColor) {
            this.ctx.strokeRect(x, y, width, height);
        }
        
        this.ctx.restore();
    }
    
    // 绘制圆形
    drawCircle(
        x: number,
        y: number,
        radius: number,
        options: ShapeOptions = {}
    ): void {
        this.ctx.save();
        
        // 应用样式选项
        if (options.fillColor) this.ctx.fillStyle = options.fillColor;
        if (options.strokeColor) this.ctx.strokeStyle = options.strokeColor;
        if (options.lineWidth) this.ctx.lineWidth = options.lineWidth;
        
        // 绘制圆形
        this.ctx.beginPath();
        this.ctx.arc(x, y, radius, 0, Math.PI * 2);
        
        if (options.fillColor) {
            this.ctx.fill();
        }
        if (options.strokeColor) {
            this.ctx.stroke();
        }
        
        this.ctx.restore();
    }
    
    // 绘制文本
    drawText(
        text: string,
        x: number,
        y: number,
        options: TextOptions = {}
    ): void {
        this.ctx.save();
        
        // 应用样式选项
        if (options.font) this.ctx.font = options.font;
        if (options.color) this.ctx.fillStyle = options.color;
        if (options.align) this.ctx.textAlign = options.align;
        if (options.baseline) this.ctx.textBaseline = options.baseline;
        
        // 绘制文本
        this.ctx.fillText(text, x, y);
        
        this.ctx.restore();
    }
}

// 绘图选项接口
interface LineOptions {
    color?: string;
    width?: number;
    dash?: number[];
}

interface ShapeOptions {
    fillColor?: string;
    strokeColor?: string;
    lineWidth?: number;
}

interface TextOptions {
    font?: string;
    color?: string;
    align?: CanvasTextAlign;
    baseline?: CanvasTextBaseline;
}

// 使用示例
const canvas = document.getElementById('canvas') as HTMLCanvasElement;
const renderer = new CanvasRenderer(canvas);

// 绘制图形
renderer.drawRect(50, 50, 100, 80, {
    fillColor: '#f0f0f0',
    strokeColor: '#333'
});

renderer.drawCircle(200, 100, 40, {
    fillColor: '#1890ff'
});

renderer.drawLine(50, 200, 250, 200, {
    color: '#666',
    width: 2,
    dash: [5, 5]
});

renderer.drawText('Hello Canvas', 100, 250, {
    font: '20px Arial',
    color: '#333',
    align: 'center'
});

动画实现

// 动画管理器
class AnimationManager {
    private animations: Animation[];
    private isRunning: boolean;
    private lastTime: number;
    
    constructor() {
        this.animations = [];
        this.isRunning = false;
        this.lastTime = 0;
        
        this.animate = this.animate.bind(this);
    }
    
    // 添加动画
    addAnimation(animation: Animation): void {
        this.animations.push(animation);
        
        if (!this.isRunning) {
            this.start();
        }
    }
    
    // 移除动画
    removeAnimation(animation: Animation): void {
        const index = this.animations.indexOf(animation);
        if (index !== -1) {
            this.animations.splice(index, 1);
        }
        
        if (this.animations.length === 0) {
            this.stop();
        }
    }
    
    // 启动动画循环
    private start(): void {
        this.isRunning = true;
        this.lastTime = performance.now();
        requestAnimationFrame(this.animate);
    }
    
    // 停止动画循环
    private stop(): void {
        this.isRunning = false;
    }
    
    // 动画循环
    private animate(currentTime: number): void {
        if (!this.isRunning) return;
        
        // 计算时间增量
        const deltaTime = currentTime - this.lastTime;
        this.lastTime = currentTime;
        
        // 更新所有动画
        this.animations.forEach(animation => {
            animation.update(deltaTime);
        });
        
        // 继续动画循环
        requestAnimationFrame(this.animate);
    }
}

// 动画基类
abstract class Animation {
    protected duration: number;
    protected elapsed: number;
    protected isComplete: boolean;
    
    constructor(duration: number) {
        this.duration = duration;
        this.elapsed = 0;
        this.isComplete = false;
    }
    
    // 更新动画状态
    update(deltaTime: number): void {
        if (this.isComplete) return;
        
        this.elapsed += deltaTime;
        
        if (this.elapsed >= this.duration) {
            this.elapsed = this.duration;
            this.isComplete = true;
        }
        
        const progress = this.elapsed / this.duration;
        this.onUpdate(this.easeInOut(progress));
    }
    
    // 缓动函数
    protected easeInOut(t: number): number {
        return t < 0.5
            ? 2 * t * t
            : -1 + (4 - 2 * t) * t;
    }
    
    // 动画更新回调
    protected abstract onUpdate(progress: number): void;
}

// 圆形动画示例
class CircleAnimation extends Animation {
    private renderer: CanvasRenderer;
    private startRadius: number;
    private endRadius: number;
    private x: number;
    private y: number;
    
    constructor(
        renderer: CanvasRenderer,
        x: number,
        y: number,
        startRadius: number,
        endRadius: number,
        duration: number
    ) {
        super(duration);
        
        this.renderer = renderer;
        this.x = x;
        this.y = y;
        this.startRadius = startRadius;
        this.endRadius = endRadius;
    }
    
    protected onUpdate(progress: number): void {
        const currentRadius = this.startRadius + (this.endRadius - this.startRadius) * progress;
        
        this.renderer.clear();
        this.renderer.drawCircle(this.x, this.y, currentRadius, {
            fillColor: '#1890ff'
        });
    }
}

// 使用示例
const canvas = document.getElementById('canvas') as HTMLCanvasElement;
const renderer = new CanvasRenderer(canvas);
const animationManager = new AnimationManager();

// 创建并添加动画
const circleAnimation = new CircleAnimation(
    renderer,
    200,
    200,
    0,
    100,
    1000 // 1秒
);

animationManager.addAnimation(circleAnimation);

SVG图形绘制

SVG基础组件

// SVG渲染器
class SVGRenderer {
    private svg: SVGSVGElement;
    
    constructor(container: HTMLElement, width: number, height: number) {
        this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        this.svg.setAttribute('width', width.toString());
        this.svg.setAttribute('height', height.toString());
        this.svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
        
        container.appendChild(this.svg);
    }
    
    // 创建矩形
    createRect(
        x: number,
        y: number,
        width: number,
        height: number,
        options: SVGShapeOptions = {}
    ): SVGRectElement {
        const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
        
        rect.setAttribute('x', x.toString());
        rect.setAttribute('y', y.toString());
        rect.setAttribute('width', width.toString());
        rect.setAttribute('height', height.toString());
        
        this.applyShapeOptions(rect, options);
        
        this.svg.appendChild(rect);
        return rect;
    }
    
    // 创建圆形
    createCircle(
        cx: number,
        cy: number,
        r: number,
        options: SVGShapeOptions = {}
    ): SVGCircleElement {
        const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        
        circle.setAttribute('cx', cx.toString());
        circle.setAttribute('cy', cy.toString());
        circle.setAttribute('r', r.toString());
        
        this.applyShapeOptions(circle, options);
        
        this.svg.appendChild(circle);
        return circle;
    }
    
    // 创建路径
    createPath(
        d: string,
        options: SVGShapeOptions = {}
    ): SVGPathElement {
        const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        
        path.setAttribute('d', d);
        
        this.applyShapeOptions(path, options);
        
        this.svg.appendChild(path);
        return path;
    }
    
    // 创建文本
    createText(
        x: number,
        y: number,
        text: string,
        options: SVGTextOptions = {}
    ): SVGTextElement {
        const textElement = document.createElementNS('http://www.w3.org/2000/svg', 'text');
        
        textElement.setAttribute('x', x.toString());
        textElement.setAttribute('y', y.toString());
        textElement.textContent = text;
        
        if (options.fontSize) {
            textElement.style.fontSize = options.fontSize;
        }
        if (options.fontFamily) {
            textElement.style.fontFamily = options.fontFamily;
        }
        if (options.fill) {
            textElement.setAttribute('fill', options.fill);
        }
        if (options.textAnchor) {
            textElement.setAttribute('text-anchor', options.textAnchor);
        }
        
        this.svg.appendChild(textElement);
        return textElement;
    }
    
    // 应用形状样式选项
    private applyShapeOptions(
        element: SVGElement,
        options: SVGShapeOptions
    ): void {
        if (options.fill) {
            element.setAttribute('fill', options.fill);
        }
        if (options.stroke) {
            element.setAttribute('stroke', options.stroke);
        }
        if (options.strokeWidth) {
            element.setAttribute('stroke-width', options.strokeWidth.toString());
        }
        if (options.opacity) {
            element.setAttribute('opacity', options.opacity.toString());
        }
    }
}

// SVG样式选项接口
interface SVGShapeOptions {
    fill?: string;
    stroke?: string;
    strokeWidth?: number;
    opacity?: number;
}

interface SVGTextOptions {
    fontSize?: string;
    fontFamily?: string;
    fill?: string;
    textAnchor?: 'start' | 'middle' | 'end';
}

// 使用示例
const container = document.getElementById('container')!;
const renderer = new SVGRenderer(container, 400, 300);

// 创建各种SVG图形
renderer.createRect(50, 50, 100, 80, {
    fill: '#f0f0f0',
    stroke: '#333',
    strokeWidth: 2
});

renderer.createCircle(200, 100, 40, {
    fill: '#1890ff',
    opacity: 0.8
});

renderer.createPath('M100,100 L200,100 L150,50 Z', {
    fill: '#666',
    stroke: '#333',
    strokeWidth: 1
});

renderer.createText(150, 200, 'Hello SVG', {
    fontSize: '20px',
    fontFamily: 'Arial',
    fill: '#333',
    textAnchor: 'middle'
});

数据可视化实现

柱状图实现

// 柱状图渲染器
class BarChart {
    private svg: SVGRenderer;
    private width: number;
    private height: number;
    private padding: number;
    
    constructor(
        container: HTMLElement,
        width: number,
        height: number,
        padding: number = 40
    ) {
        this.width = width;
        this.height = height;
        this.padding = padding;
        
        this.svg = new SVGRenderer(container, width, height);
    }
    
    // 渲染柱状图
    render(data: BarData[]): void {
        // 计算坐标轴范围
        const maxValue = Math.max(...data.map(d => d.value));
        const chartWidth = this.width - 2 * this.padding;
        const chartHeight = this.height - 2 * this.padding;
        const barWidth = chartWidth / data.length * 0.8;
        const barGap = chartWidth / data.length * 0.2;
        
        // 绘制坐标轴
        this.drawAxis(chartWidth, chartHeight, maxValue);
        
        // 绘制柱子
        data.forEach((item, index) => {
            const x = this.padding + index * (barWidth + barGap);
            const barHeight = (item.value / maxValue) * chartHeight;
            const y = this.height - this.padding - barHeight;
            
            // 绘制柱子
            this.svg.createRect(x, y, barWidth, barHeight, {
                fill: item.color || '#1890ff',
                opacity: 0.8
            });
            
            // 绘制标签
            this.svg.createText(
                x + barWidth / 2,
                this.height - this.padding + 20,
                item.label,
                {
                    fontSize: '12px',
                    textAnchor: 'middle'
                }
            );
            
            // 绘制数值
            this.svg.createText(
                x + barWidth / 2,
                y - 5,
                item.value.toString(),
                {
                    fontSize: '12px',
                    textAnchor: 'middle'
                }
            );
        });
    }
    
    // 绘制坐标轴
    private drawAxis(
        chartWidth: number,
        chartHeight: number,
        maxValue: number
    ): void {
        // X轴
        this.svg.createPath(
            `M${this.padding},${this.height - this.padding} ` +
            `L${this.width - this.padding},${this.height - this.padding}`,
            {
                stroke: '#666',
                strokeWidth: 1
            }
        );
        
        // Y轴
        this.svg.createPath(
            `M${this.padding},${this.padding} ` +
            `L${this.padding},${this.height - this.padding}`,
            {
                stroke: '#666',
                strokeWidth: 1
            }
        );
        
        // Y轴刻度
        const tickCount = 5;
        for (let i = 0; i <= tickCount; i++) {
            const y = this.height - this.padding - (i / tickCount) * chartHeight;
            const value = Math.round(maxValue * (i / tickCount));
            
            // 刻度线
            this.svg.createPath(
                `M${this.padding - 5},${y} L${this.padding},${y}`,
                {
                    stroke: '#666',
                    strokeWidth: 1
                }
            );
            
            // 刻度值
            this.svg.createText(
                this.padding - 10,
                y,
                value.toString(),
                {
                    fontSize: '12px',
                    textAnchor: 'end'
                }
            );
        }
    }
}

// 数据接口
interface BarData {
    label: string;
    value: number;
    color?: string;
}

// 使用示例
const container = document.getElementById('chart-container')!;
const chart = new BarChart(container, 600, 400);

const data: BarData[] = [
    { label: '一月', value: 120, color: '#1890ff' },
    { label: '二月', value: 200, color: '#2fc25b' },
    { label: '三月', value: 150, color: '#facc14' },
    { label: '四月', value: 180, color: '#223273' },
    { label: '五月', value: 240, color: '#8543e0' }
];

chart.render(data);

饼图实现

// 饼图渲染器
class PieChart {
    private svg: SVGRenderer;
    private width: number;
    private height: number;
    private radius: number;
    
    constructor(
        container: HTMLElement,
        width: number,
        height: number
    ) {
        this.width = width;
        this.height = height;
        this.radius = Math.min(width, height) / 3;
        
        this.svg = new SVGRenderer(container, width, height);
    }
    
    // 渲染饼图
    render(data: PieData[]): void {
        const total = data.reduce((sum, item) => sum + item.value, 0);
        let startAngle = 0;
        
        // 绘制扇形
        data.forEach(item => {
            const percentage = item.value / total;
            const endAngle = startAngle + percentage * Math.PI * 2;
            
            // 计算扇形路径
            const path = this.createArcPath(
                this.width / 2,
                this.height / 2,
                this.radius,
                startAngle,
                endAngle
            );
            
            // 绘制扇形
            this.svg.createPath(path, {
                fill: item.color || '#1890ff',
                stroke: '#fff',
                strokeWidth: 1
            });
            
            // 计算标签位置
            const labelAngle = startAngle + (endAngle - startAngle) / 2;
            const labelRadius = this.radius * 1.2;
            const labelX = this.width / 2 + Math.cos(labelAngle) * labelRadius;
            const labelY = this.height / 2 + Math.sin(labelAngle) * labelRadius;
            
            // 绘制标签
            this.svg.createText(
                labelX,
                labelY,
                `${item.label} (${Math.round(percentage * 100)}%)`,
                {
                    fontSize: '12px',
                    textAnchor: 'middle'
                }
            );
            
            startAngle = endAngle;
        });
    }
    
    // 创建扇形路径
    private createArcPath(
        cx: number,
        cy: number,
        radius: number,
        startAngle: number,
        endAngle: number
    ): string {
        const start = {
            x: cx + Math.cos(startAngle) * radius,
            y: cy + Math.sin(startAngle) * radius
        };
        
        const end = {
            x: cx + Math.cos(endAngle) * radius,
            y: cy + Math.sin(endAngle) * radius
        };
        
        const largeArcFlag = endAngle - startAngle <= Math.PI ? '0' : '1';
        
        return [
            'M', cx, cy,
            'L', start.x, start.y,
            'A', radius, radius, 0, largeArcFlag, 1, end.x, end.y,
            'Z'
        ].join(' ');
    }
}

// 数据接口
interface PieData {
    label: string;
    value: number;
    color?: string;
}

// 使用示例
const container = document.getElementById('pie-container')!;
const chart = new PieChart(container, 400, 400);

const data: PieData[] = [
    { label: '产品A', value: 30, color: '#1890ff' },
    { label: '产品B', value: 20, color: '#2fc25b' },
    { label: '产品C', value: 25, color: '#facc14' },
    { label: '产品D', value: 15, color: '#223273' },
    { label: '产品E', value: 10, color: '#8543e0' }
];

chart.render(data);

最佳实践与建议

  1. 性能优化

    • 使用适当的渲染技术
    • 实现图形缓存
    • 优化动画性能
    • 控制重绘频率
  2. 代码组织

    • 模块化设计
    • 组件封装
    • 统一接口
    • 类型定义
  3. 用户体验

    • 流畅的动画
    • 交互响应
    • 适当的提示
    • 错误处理
  4. 可维护性

    • 清晰的架构
    • 完善的文档
    • 单元测试
    • 代码规范

总结

前端可视化开发需要考虑以下方面:

  1. 选择合适的可视化技术
  2. 设计清晰的架构
  3. 实现高效的渲染
  4. 优化性能和体验
  5. 保持代码可维护性

通过合理的技术选型和架构设计,可以构建出高性能、易用的可视化应用。

学习资源

  1. Canvas API文档
  2. SVG开发指南
  3. WebGL教程
  4. 数据可视化最佳实践
  5. 性能优化技巧

如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻

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

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

相关文章

源码:用Python进行电影数据分析实战指南

源码&#xff1a;用Python进行电影数据分析实战指南 原创 IT小本本 IT小本本 2025年03月03日 22:28 北京 接上一篇文章&#xff1a;用Python进行电影数据分析实战指南 1、首先复制csv内容到csv文件中 2、接着创建.py文件复制源码内容 3、运行代码&#xff0c;就可以看到数据…

GHCTF2025--Web

upload?SSTI! import os import refrom flask import Flask, request, jsonify,render_template_string,send_from_directory, abort,redirect from werkzeug.utils import secure_filename import os from werkzeug.utils import secure_filenameapp Flask(__name__)# 配置…

Unity Shader编程】之基础纹理

一&#xff0c;单张纹理 好的&#xff0c;用户想学习Unity Shader中的单张纹理章节。我需要根据提供的搜索结果来整理相关内容。首先&#xff0c;查看搜索结果中的相关部分&#xff0c;特别是‌、‌、‌、‌、‌这几条&#xff0c;因为它们提到了基础纹理、单张纹理的实现方法…

SpringBoot使用注解扫描注册Java Web三大组件

使用注解扫描和注册Java Web三大组件&#xff08;Servlet、Filter、Listener&#xff09;非常方便。 1. Servlet 注册 Servlet 是 Java Web 开发的基础组件&#xff0c;用于处理客户端&#xff08;通常是浏览器&#xff09;发送的 HTTP 请求并生成响应。 Controller是基于 Ser…

STM32F4 UDP组播通信:填一填ST官方HAL库的坑

先说写作本文的原因&#xff0c;由于开项目开发中需要用到UDP组播接收的功能&#xff0c;但是ST官方没有提供合适的参考&#xff0c;使用STM32CubeMX生成的代码也是不能直接使用的&#xff0c;而我在网上找了一大圈&#xff0c;也没有一个能够直接解决的方案&#xff0c;deepse…

JVM - 3.垃圾回收

1.垃圾收集的经典问题 1.哪些内存需要回收2.什么时候回收3.如何回收1.你知道哪几种垃圾回收器&#xff0c;各自的优缺点&#xff0c;重点讲一下cms和g12.JVM GC算法有哪些&#xff0c;目前的JDK版本采用什么回收算法3.G1回收器的回收过程 1.Java中垃圾的定义&#xff08;Garbag…

重构谷粒商城09:人人开源框架的快速入门

谷粒商城09——人人开源框架的快速入门 前言&#xff1a;这个系列将使用最前沿的cursor作为辅助编程工具&#xff0c;来快速开发一些基础的编程项目。目的是为了在真实项目中&#xff0c;帮助初级程序员快速进阶&#xff0c;以最快的速度&#xff0c;效率&#xff0c;快速进阶…

css实现元素垂直居中显示的7种方式

文章目录 * [【一】知道居中元素的宽高](https://blog.csdn.net/weixin_41305441/article/details/89886846#_1) [absolute 负margin](https://blog.csdn.net/weixin_41305441/article/details/89886846#absolute__margin_2) [absolute margin auto](https://blog.csdn.net…

用Python写一个算24点的小程序

一、运行界面 二、显示答案——递归介绍 工作流程&#xff1a; 1. 基本情况&#xff1a;函数首先检查输入的数字列表 nums 的长度。如果列表中只剩下一个数字&#xff0c;它会判断这个数字是否接近 24&#xff08;使用 abs(nums[0] - 24) < 1e-10 来处理浮点数精度问题&…

【长安大学】苹果手机/平板自动连接认证CHD-WIFI脚本(快捷指令)

背景&#xff1a; 已经用这个脚本的记得设置Wifi时候&#xff0c;关闭“自动登录” 前几天实在忍受不了CHD-WIFI动不动就断开&#xff0c;一天要重新连接&#xff0c;点登陆好几次。试了下在网上搜有没有CHD-WIFI的自动连接WIFI自动认证脚本&#xff0c;那样我就可以解放双手&…

双击PPT文件界面灰色不可用,需要再次打开该PPT文件才能正常打开

双击PPT文件界面灰色不可用&#xff0c;需要再次打开该PPT文件才能正常打开 1. 软件环境⚙️2. 问题描述&#x1f50d;3. 解决方法&#x1f421;解决步骤 4. 结果预览&#x1f914; 1. 软件环境⚙️ Windows10 或 Windows11 专业版64位&#xff0c;安装MotionGo软件&#xff08…

蓝桥杯[每日两题] 真题:好数 神奇闹钟 (java版)

题目一&#xff1a;好数 题目描述 一个整数如果按从低位到高位的顺序&#xff0c;奇数位&#xff08;个位、百位、万位 &#xff09;上的数字是奇数&#xff0c;偶数位&#xff08;十位、千位、十万位 &#xff09;上的数字是偶数&#xff0c;我们就称之为“好数”。给定…

蓝桥杯刷题周计划(第二周)

目录 前言题目一题目代码题解分析 题目二题目代码题解分析 题目三题目代码题解分析 题目四题目代码题解分析 题目五题目代码题解分析 题目六题目代码题解分析 题目七题目代码题解分析 题目八题目题解分析 题目九题目代码题解分析 题目十题目代码题解分析 题目十一题目代码题解分…

ThinkPHP框架

在电脑C磁盘中安装composer 命令 在电脑的D盘中创建cd文件夹 切换磁盘 创建tp框架 创建一个aa的网站&#xff0c;更换路径到上一步下载的tp框架路径 在管理中修改路径 下载压缩包public和view 将前面代码中的public和view文件替换 在PHPStom 中打开文件 运行指定路径 修改demo…

Spring学习笔记:工厂模式与反射机制实现解耦

1.什么是Spring? spring是一个开源轻量级的java开发应用框架&#xff0c;可以简化企业级应用开发 轻量级 1.轻量级(对于运行环境没有额外要求) 2.代码移植性高(不需要实现额外接口) JavaEE的解决方案 Spring更像是一种解决方案&#xff0c;对于控制层&#xff0c;它有Spring…

爬虫案例八js逆向爬取网易音乐

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、js逆向的前期准备二、网站分析三、代码 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 爬取网易音乐 提示&#xff1a;以下是本篇…

【02】Web网页基础

一、网页的组成 1、HTML 2、CSS 3、javascript 二、网页的结构 三、节点树及节点间的关系 四、选择器 一、网页的组成 网页可以分为三大部分 —— HTML、CSS 和 JavaScript。如果把网页比作一个人的话&#xff0c;HTML 相当于骨架&#xff0c;JavaScript 相当于肌肉&#…

Dify部署踩坑指南(Windows+Mac)

组件说明 Dify踩坑及解决方案 ⚠️ 除了修改镜像版本&#xff0c;nginx端口不要直接修改docker-compose.yaml &#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 1、更换镜像版本 这个文件是由.env自动生成的&#xff0c;在.env配置 …

​​《从事件冒泡到处理:前端事件系统的“隐形逻辑”》

“那天在document见到你的第一眼&#xff0c;我就下定决心要陪你到天荒地老” ---React 我将从事件从出现到被处理的各个过程来介绍事件机制&#xff1a; 这张图片给我们展示了react事件的各个阶段&#xff0c;我们可以看到有DOM&#xff0c;合成事件层&#xff0c;还有…

tiktok web登录 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向分析 部分代码 response reques…