modify:新增小程序

This commit is contained in:
ECRZ
2026-01-06 18:00:43 +08:00
parent 498fa0e915
commit da4a055c1c
47 changed files with 7321 additions and 61 deletions

View File

@@ -0,0 +1,90 @@
Component({
properties: {
canvasId: {
type: String,
value: 'ec-canvas'
},
ec: {
type: Object,
value: {}
},
disableTouch: {
type: Boolean,
value: false
}
},
data: {
isNew: true
},
ready() {
console.log('ec-canvas ready')
if (!this.data.ec) {
console.warn('组件需绑定 ec 对象')
return
}
if (!this.data.ec.onInit) {
console.warn('ec 对象需包含 onInit 方法')
return
}
const query = this.createSelectorQuery()
query.select(`#${this.data.canvasId}`)
.fields({ node: true, size: true })
.exec((res) => {
console.log('Canvas query 结果:', res)
if (!res || !res[0]) {
console.error('Canvas 节点未找到')
return
}
const canvasNode = res[0].node
const ctx = canvasNode.getContext('2d')
const dpr = wx.getSystemInfoSync().pixelRatio
const width = res[0].width
const height = res[0].height
console.log('Canvas 尺寸信息:', { width, height, dpr })
canvasNode.width = width * dpr
canvasNode.height = height * dpr
ctx.scale(dpr, dpr)
const canvas = {
width: width * dpr, // 使用缩放后的宽度
height: height * dpr, // 使用缩放后的高度
getContext: () => ctx,
node: canvasNode
}
console.log('准备调用 onInitcanvas 对象:', canvas)
this.chart = this.data.ec.onInit(canvas, width, height, res)
console.log('onInit 返回的 chart:', this.chart)
})
},
methods: {
touchStart(e) {
if (this.chart && this.chart.touchStart) {
this.chart.touchStart(e)
}
},
touchMove(e) {
if (this.chart && this.chart.touchMove) {
this.chart.touchMove(e)
}
},
touchEnd(e) {
if (this.chart && this.chart.touchEnd) {
this.chart.touchEnd(e)
}
}
}
})

View File

@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@@ -0,0 +1,10 @@
<canvas
type="2d"
canvas-id="{{ canvasId }}"
id="{{ canvasId }}"
class="ec-canvas"
bindinit="init"
bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}"
bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}"
bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"
></canvas>

View File

@@ -0,0 +1,4 @@
.ec-canvas {
width: 100%;
height: 100%;
}

View File

@@ -0,0 +1,242 @@
/**
* 简化版 ECharts - 仅支持折线图
* 用于微信小程序 Canvas 2D
*/
class ECharts {
constructor(canvas) {
this.canvas = canvas
this.ctx = canvas.getContext('2d')
this.option = null
// canvas.width 已经是缩放后的尺寸,直接使用
this.width = canvas.width || 750
this.height = canvas.height || 500
this.padding = { top: 40, right: 40, bottom: 60, left: 80 }
console.log('ECharts 构造函数:', {
canvasWidth: canvas.width,
canvasHeight: canvas.height,
chartWidth: this.width,
chartHeight: this.height
})
}
setOption(option) {
this.option = option
this.render()
}
clear() {
if (!this.ctx) return
this.ctx.clearRect(0, 0, this.width, this.height)
}
resize() {
// 自动调整大小
}
render() {
if (!this.option || !this.ctx) {
console.log('ECharts render: 缺少 option 或 ctx')
return
}
const { xAxis, yAxis, series } = this.option
if (!series || !series[0]) {
console.log('ECharts render: 缺少 series 数据')
return
}
const data = series[0].data || []
const categories = xAxis?.data || []
console.log('ECharts render:', {
dataCount: data.length,
categoryCount: categories.length,
width: this.width,
height: this.height
})
this.clear()
// 计算绘图区域
const chartWidth = this.width - this.padding.left - this.padding.right
const chartHeight = this.height - this.padding.top - this.padding.bottom
// 计算数据范围
const maxValue = Math.max(...data) * 1.1
const minValue = Math.min(...data) * 0.9
const valueRange = maxValue - minValue || 1
// 绘制坐标轴
this.drawAxes(chartWidth, chartHeight, minValue, maxValue, categories)
// 绘制区域填充
if (series[0].areaStyle) {
this.drawArea(data, chartWidth, chartHeight, minValue, valueRange)
}
// 绘制折线
this.drawLine(data, chartWidth, chartHeight, minValue, valueRange)
// 绘制数据点
this.drawPoints(data, chartWidth, chartHeight, minValue, valueRange)
}
drawAxes(chartWidth, chartHeight, minValue, maxValue, categories) {
const ctx = this.ctx
const { top, right, bottom, left } = this.padding
ctx.strokeStyle = '#e0e0e0'
ctx.lineWidth = 1
// X 轴
ctx.beginPath()
ctx.moveTo(left, top + chartHeight)
ctx.lineTo(left + chartWidth, top + chartHeight)
ctx.stroke()
// Y 轴
ctx.beginPath()
ctx.moveTo(left, top)
ctx.lineTo(left, top + chartHeight)
ctx.stroke()
// 绘制 Y 轴刻度
ctx.fillStyle = '#666'
ctx.font = '20px sans-serif'
ctx.textAlign = 'right'
ctx.textBaseline = 'middle'
const ySteps = 5
for (let i = 0; i <= ySteps; i++) {
const value = minValue + (maxValue - minValue) * (i / ySteps)
const y = top + chartHeight - (chartHeight * (i / ySteps))
ctx.fillText(
'¥' + Math.round(value),
left - 10,
y
)
// 绘制网格线
ctx.strokeStyle = '#f0f0f0'
ctx.beginPath()
ctx.moveTo(left, y)
ctx.lineTo(left + chartWidth, y)
ctx.stroke()
}
// 绘制 X 轴标签
ctx.textAlign = 'center'
ctx.textBaseline = 'top'
const xStep = chartWidth / (categories.length || 1)
const skipStep = Math.ceil(categories.length / 6) // 最多显示6个标签
categories.forEach((category, index) => {
if (index % skipStep !== 0) return // 跳过部分标签
const x = left + xStep * (index + 0.5)
const y = top + chartHeight + 10
ctx.save()
ctx.translate(x, y)
ctx.rotate(45 * Math.PI / 180)
ctx.fillText(category, 0, 0)
ctx.restore()
})
}
drawLine(data, chartWidth, chartHeight, minValue, valueRange) {
const ctx = this.ctx
const { top, left } = this.padding
const xStep = chartWidth / (data.length || 1)
ctx.strokeStyle = '#1890ff'
ctx.lineWidth = 3
ctx.lineCap = 'round'
ctx.lineJoin = 'round'
ctx.beginPath()
data.forEach((value, index) => {
const x = left + xStep * (index + 0.5)
const y = top + chartHeight - ((value - minValue) / valueRange) * chartHeight
if (index === 0) {
ctx.moveTo(x, y)
} else {
ctx.lineTo(x, y)
}
})
ctx.stroke()
}
drawArea(data, chartWidth, chartHeight, minValue, valueRange) {
const ctx = this.ctx
const { top, left } = this.padding
const xStep = chartWidth / (data.length || 1)
// 创建渐变
const gradient = ctx.createLinearGradient(0, top, 0, top + chartHeight)
gradient.addColorStop(0, 'rgba(24, 144, 255, 0.3)')
gradient.addColorStop(1, 'rgba(24, 144, 255, 0.05)')
ctx.fillStyle = gradient
ctx.beginPath()
data.forEach((value, index) => {
const x = left + xStep * (index + 0.5)
const y = top + chartHeight - ((value - minValue) / valueRange) * chartHeight
if (index === 0) {
ctx.moveTo(x, y)
} else {
ctx.lineTo(x, y)
}
})
// 闭合路径
const lastX = left + xStep * (data.length - 0.5)
ctx.lineTo(lastX, top + chartHeight)
ctx.lineTo(left + xStep * 0.5, top + chartHeight)
ctx.closePath()
ctx.fill()
}
drawPoints(data, chartWidth, chartHeight, minValue, valueRange) {
const ctx = this.ctx
const { top, left } = this.padding
const xStep = chartWidth / (data.length || 1)
data.forEach((value, index) => {
const x = left + xStep * (index + 0.5)
const y = top + chartHeight - ((value - minValue) / valueRange) * chartHeight
// 绘制点
ctx.fillStyle = '#fff'
ctx.strokeStyle = '#1890ff'
ctx.lineWidth = 2
ctx.beginPath()
ctx.arc(x, y, 4, 0, 2 * Math.PI)
ctx.fill()
ctx.stroke()
})
}
}
function init(canvas, width, height) {
const chart = new ECharts(canvas)
return chart
}
module.exports = {
init
}

View File

@@ -0,0 +1,61 @@
export default class WxCanvas {
constructor(ctx, canvasId, isNew, canvasNode) {
this.ctx = ctx
this.canvasId = canvasId
this.chart = null
this.isNew = isNew
if (isNew) {
this.canvasNode = canvasNode
}
this.chart = null
this._init()
}
getContext(contextType) {
return this.ctx
}
setChart(chart) {
this.chart = chart
}
addEventListener() {
// 暂不支持事件监听
}
removeEventListener() {
// 暂不支持事件监听
}
_init() {
const dpr = wx.getSystemInfoSync().pixelRatio
this.ctx.scale(dpr, dpr)
}
// 代理 canvas 方法
setWidth(width) {
// Canvas 2D 不需要手动设置
}
setHeight(height) {
// Canvas 2D 不需要手动设置
}
getWidth() {
return this.canvasNode.width
}
getHeight() {
return this.canvasNode.height
}
addEvtListener() {
// 事件监听
}
removeEvtListener() {
// 移除监听
}
}