[Html5][Canvas]Draw a column chart

本文详细介绍了如何使用HTML5 Canvas在Column.html中设计数据结构并创建柱状图表,包括数据准备、图形绘制步骤,以及如何通过JavaScript动态读取文件并显示数据。适合初学者学习数据可视化基础。


1.design the data, the structure should be like shown in this picture, and put them in a separate file.
2.draw a column chart to display the data (you don’t need to put the text for now)


一、效果预览

初始画布
选择data文件
生成条形图
在这里插入图片描述

二、Column.html

代码如下(示例):

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH1Ex4:Draw a column chart</title>

</head>
<body>
    <input id="input" type="file"><br>
	<canvas id="canvasOne" width="1080" height="480">
 		Your browser does not support HTML 5 Canvas. 
	</canvas>
</body>
  
<script type="text/javascript">
    // 初始化canvas
	var MyCanvas = document.getElementById("canvasOne");
	var ctx = MyCanvas.getContext("2d");
    ctx.fillStyle="#EEC900";
    ctx.fillRect(0,0,1080,480);

    
    var recWidth = 40;  // 默认矩形宽度
    var recColor1 = "#7AC5CD";  // 默认矩形颜色
    var recColor2 = "#EE7942";
    var recColor3 = "#8FBC8F";
    var a = 10;  // 默认数据转换倍率
    var x = 20;  // 初始绘制x坐标
    var y = 400;  // 初始绘制y坐标
    var rul = 5;  // 绘制标尺间隔长度

    ctx.fillStyle="#F5F5F5";
    ctx.font="15px Arial";
    for(var i = 0; i < 7;i++){
        ctx.fillText(0+i*rul,x,y-i*rul*a);
        ctx.fillRect(x+20,y-i*rul*a,1080,2);
    }
    

    function drawRec(ctx,name,num1,num2,num3){
        
        // 参数name的文本绘制
        ctx.fillStyle="#fff";
        ctx.font="30px Arial";
        ctx.fillText(name,x+recWidth*2+40,y+30);

        // 参数1、2、3的矩形绘制
        ctx.fillStyle=recColor1;
        ctx.fillRect(x=x+recWidth+20,y,recWidth,-num1*a);
        ctx.fillStyle=recColor2;
        ctx.fillRect(x=x+recWidth+20,y,recWidth,-num2*a);
        ctx.fillStyle=recColor3;
        ctx.fillRect(x=x+recWidth+20,y,recWidth,-num3*a);
        x+=recWidth*2;
    }
    

    // 本地文件读取数据
    const input = document.querySelector('input[type=file]')
    input.addEventListener('change', ()=>{
        const reader = new FileReader()
        reader.readAsText(input.files[0],'utf8')
        reader.onload = ()=>{

            // 分行读取文件数据到lines数组
            var lines = reader.result.split('\n');

            // 遍历所有行数
            var i = 1;
            while(lines[i]!=null){
                // 将每行的数据分别存到nums
                var nums = lines[i].split('      '); 

                // 绘制条形图
                drawRec(ctx,nums[0],nums[1],nums[2],nums[3]);
                i++;
            }
        }
    }, false)
    
</script>

</html>
import tkinter as tk from tkinter import ttk import random import datetime import calendar class SalesChartApp: def __init__(self, root): self.root = root self.root.title("年度销售额统计") self.root.geometry("900x600") # 初始化数据存储 self.current_year = datetime.datetime.now().year self.sales_data = {year: [0] * 12 for year in range(self.current_year - 2, self.current_year + 1)} # 创建控制面板 control_frame = ttk.Frame(root, padding=10) control_frame.pack(fill=tk.X) # 年份选择 ttk.Label(control_frame, text="选择年份:").grid(row=0, column=0, padx=5) self.year_var = tk.IntVar(value=self.current_year) years = list(range(self.current_year - 2, self.current_year + 1)) self.year_combo = ttk.Combobox( control_frame, textvariable=self.year_var, values=years, state="readonly", width=8 ) self.year_combo.grid(row=0, column=1, padx=5) self.year_combo.bind("<<ComboboxSelected>>", self.update_chart) # 自动更新按钮 self.auto_update_var = tk.BooleanVar(value=True) auto_update_btn = ttk.Checkbutton( control_frame, text="自动年度更新", variable=self.auto_update_var ) auto_update_btn.grid(row=0, column=2, padx=20) # 添加模拟数据按钮 ttk.Button( control_frame, text="生成模拟数据", command=self.generate_data ).grid(row=0, column=3, padx=5) # 创建图表画布 self.chart_canvas = tk.Canvas(root, bg="white", height=500) self.chart_canvas.pack(fill=tk.BOTH, expand=True, padx=20, pady=10) # 初始化数据并绘制图表 self.generate_data() self.update_chart() # 设置年度自动更新 self.schedule_annual_update() def generate_data(self): """生成模拟销售数据""" for year in self.sales_data: self.sales_data[year] = [random.randint(8000, 20000) for _ in range(12)] def update_chart(self, event=None): """更新图表显示""" selected_year = self.year_var.get() self.draw_bar_chart(selected_year) def draw_bar_chart(self, year): """在画布上绘制条形图""" self.chart_canvas.delete("all") data = self.sales_data[year] # 获取画布尺寸 canvas_width = self.chart_canvas.winfo_width() canvas_height = self.chart_canvas.winfo_height() if canvas_width <= 1 or canvas_height <= 1: return # 防止初始化时尺寸错误 # 图表参数 margin = 60 chart_width = canvas_width - 2 * margin chart_height = canvas_height - 2 * margin bar_width = chart_width / 15 gap = bar_width / 2 # 计算最大值用于缩放 max_value = max(data) * 1.1 # 绘制坐标轴 self.chart_canvas.create_line(margin, margin, margin, canvas_height - margin, width=2) # Y轴 self.chart_canvas.create_line(margin, canvas_height - margin, canvas_width - margin, canvas_height - margin, width=2) # X轴 # 绘制标题 title = f"{year}年各月销售额统计(单位:元)" self.chart_canvas.create_text(canvas_width/2, 20, text=title, font=("Arial", 14, "bold")) # 绘制Y轴刻度和标签 for i in range(6): y = canvas_height - margin - (i * chart_height / 5) value = int(max_value * i / 5) self.chart_canvas.create_line(margin - 5, y, margin, y, width=1) self.chart_canvas.create_text(margin - 10, y, text=f"{value:,}", anchor=tk.E, font=("Arial", 9)) # 绘制条形和标签 for i, month in enumerate(calendar.month_abbr[1:]): # 跳过空值 # 计算条形位置 x1 = margin + gap + i * (bar_width + gap) x2 = x1 + bar_width y1 = canvas_height - margin y2 = canvas_height - margin - (data[i] / max_value) * chart_height # 绘制条形 bar_color = "#4e79a7" # 蓝色 self.chart_canvas.create_rectangle(x1, y1, x2, y2, fill=bar_color, outline="black") # 显示月份 self.chart_canvas.create_text( (x1 + x2) / 2, canvas_height - margin + 15, text=month, font=("Arial", 10) ) # 显示销售额数值 self.chart_canvas.create_text( (x1 + x2) / 2, y2 - 15, text=f"{data[i]:,}", font=("Arial", 9), fill="white" ) # 添加悬停效果 tag_name = f"bar_{i}" self.chart_canvas.create_rectangle( x1, y1, x2, y2, fill="", outline="", tags=(tag_name,) ) self.chart_canvas.tag_bind( tag_name, "<Enter>", lambda e, val=data[i], month=month: self.show_tooltip(e, val, month) ) self.chart_canvas.tag_bind(tag_name, "<Leave>", self.hide_tooltip) def show_tooltip(self, event, value, month): """显示悬停提示""" self.tooltip = tk.Toplevel(self.root) self.tooltip.wm_overrideredirect(True) self.tooltip.wm_geometry(f"+{event.x_root+10}+{event.y_root+10}") label = tk.Label( self.tooltip, text=f"{month}: ¥{value:,}", bg="lightyellow", relief="solid", borderwidth=1 ) label.pack() def hide_tooltip(self, event): """隐藏悬停提示""" if hasattr(self, 'tooltip') and self.tooltip: self.tooltip.destroy() def schedule_annual_update(self): """安排年度自动更新任务""" now = datetime.datetime.now() # 计算到明年1月1日的时间(毫秒) next_year = now.year + 1 next_january = datetime.datetime(next_year, 1, 1) delay_ms = int((next_january - now).total_seconds() * 1000) # 安排更新任务 self.root.after(delay_ms, self.annual_update) def annual_update(self): """年度自动更新""" if self.auto_update_var.get(): # 更新年份范围 self.current_year += 1 new_years = list(range(self.current_year - 2, self.current_year + 1)) # 添加新一年的数据 self.sales_data[self.current_year] = [random.randint(8000, 20000) for _ in range(12)] # 移除旧数据(保留最近3年) for year in list(self.sales_data.keys()): if year < self.current_year - 2: del self.sales_data[year] # 更新下拉框 self.year_combo['values'] = new_years self.year_var.set(self.current_year) # 更新图表 self.update_chart() # 重新安排下一次更新 self.schedule_annual_update() if __name__ == "__main__": root = tk.Tk() app = SalesChartApp(root) root.mainloop() 加上填写数据
07-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值