本页面将系统讲述如何使用function-plot进行函数编辑
Important
在阅读本页面之前,请确保你以及阅读并理解了 帮助-语法 帮助-页面css与js 帮助-页面操作
function-plot 是一个基于 D3.js 的绘图库,旨在以极少的配置来渲染函数(可以想象成是 Google 绘图工具的克隆: )。
该库目前支持交互式折线图和散点图,每当图形的比例被修改时,函数会使用新的边界再次进行评估,结果:无限图形!
function-plot 与其他使用均匀分布的点通过线段连接的绘图工具不同,它使用区间算术来正确确定需要用少量样本绘制的屏幕区域。
大多数简单的绘图工具在绘制快速振荡的函数时会遇到问题,例如 当 时振荡迅速,无论函数被评估多少次,我们都无法正确渲染这个函数。
Function Plot 将使用区间算术数学来评估函数,这意味着当一个矩形的 x 边界为 出现在屏幕上时,可以确保它包含所有可能的 ,其中 ,结果:曲线的像素完美表示。
function-plot的所有属性如下表所示
字段 | 描述 |
---|---|
annotations | 作平行线标注 |
title | 给画布命名 |
target | 目标 html 节点(就是div的id),用于生成画布 |
width | 画布默认宽度设置 |
height | 画布默认长度设置 |
xAxis | x轴相关设置 |
yAxis | y轴相关设置 |
disableZoom | 是否可以手动伸缩画布 |
grid | 是否显示单元格 |
id | 内置私有属性,标识,无需关注 |
plugins | 插件配置(很少使用) |
tip | 设置触摸函数某点时进行 x, y 轴虚线绘制以及 x, y 值计算结果显示 |
xDomain | 用于在多次调用 functionPlot 时保留 x 域的内部状态 |
yDomain | 用于在多次调用 functionPlot 时保留 y 域的内部状态 |
data | 需要绘制的函数 |
接下来我们将以此演示如何使用function-plot语法
这是一个插入到网页的js代码示例:
<script>
document.addEventListener("DOMContentLoaded", function() {
setupFunctionPlot({
target: '#quadratic',
data: [{
fn: 'x^2'
}]
});
});
</script>
在setupFunctionPlot函数外围 必须 使用 document.addEventListener(....) 包围
其中 setupFunctionPlot()
是创建函数图像的,它的参数和官网参数一致,在之后的教程我将只保留参数。
最简单的例子:
{
target: '#quadratic',
data: [{
fn: 'x^2'
}]
}
Warning
target 里面的内容为绑定的div的id,其实就是规定了函数渲染的位置(就在div处)
data是一个对象数组,其中包含有关要呈现的函数的信息,data.fn (string) 是要渲染的数学表达式,使用区间算术对其进行解析和评估绘制
上述渲染结果为:
上图还可以在 functionPlot({}) 里定义以下附加基础选项:
title: 请看 前面表格对应属性解释
width: 请看 前面表格对应属性解释
height:请看 前面表格对应属性解释
xAxis: 请看 前面表格对应属性解释
type: 此轴的刻度类型,可能值线性|对数(默认值:‘线性’)
label: x 轴标签
domain: x 轴左右边界默认值
yAxis: 同 xAxis
disableZoom:请看 前面表格对应属性解释
如:
{
title: 'y = x * x',
target: '#f2',
width: 580,
height: 400,
disableZoom: true,
xAxis: {
label: 'x - axis',
domain: [-6, 6]
},
yAxis: {
label: 'y - axis'
},
data: [{
fn: 'x^2'
}]
}
输出结果为:
设置 grid: true 到 setupFunctionPlot({}) 选项中
{
target: '#f3',
xAxis: {
label: 'real'
},
yAxis: {
label: 'imaginary'
},
grid: true,
data: [
{ fn: 'sqrt(1 - x * x)' },
{ fn: '-sqrt(1 - x * x)' }
]
}
输出结果为:
nSamples 确定函数将在当前域中评估的等距点的数量,增加它将更准确地使用矩形表示函数,但是太大会影响处理绘制速度(一般此参数忽略不使用)
{
target: '#f4',
data: [{
fn: 'sin(x)',
nSamples: 100
}]
}
可以在 annotations 选项中设置与 y 轴或 x 轴的平行线:
x:平行于y轴的直线的x坐标
y:平行于x轴的直线的y坐标
text(可选)显示在平行线旁边的注释
注意:需要在对象上设置 x 或 y,同时设置它们会引发异常
{
target: '#f5',
yAxis: {domain: [-1, 9]},
data: [{
fn: 'x^2'
}],
annotations: [{
x: -1
}, {
x: 1,
text: 'x = 1'
}, {
y: 2,
text: 'y = 2'
}]
}
如果未设置 range 区间范围,closed: true 时,函数区域图将取自当前x轴的正负边界。
range: 一个 2 位数的数组,该函数将仅在此范围内绘制
closed:true 将渲染闭合路径区域图,y0 将始终为 0,y1 将为
{
target: '#f6',
xAxis: {domain: [-2, 12]},
data: [{
fn: '1+2sin(x)',
range: [2, 8],
closed: true
}]
}
对数刻度(log scale)是当数据的值在一个很大范围内时,利用对数使此降低到一个更加易处理的范围。通过在 xAxis 选项中指定要记录的轴类型,可以将每个轴的类型配置为对数,请注意此更改如何影响函数的采样方式
{
target: '#f7',
xAxis: {
type: 'log',
domain: [0.01, 1]
},
yAxis: {
domain: [-100, 100]
},
grid: true,
data: [{
fn: '1/x * cos(1/x)',
// 让它看起来像一个定积分
closed: true
}]
}
上面例子中看到的data是一个数组,这意味着可以在同一个画布中绘制多个函数
{
target: '#f8',
data: [
{ fn: 'x', color: 'pink' },
{ fn: '-x' },
{ fn: 'x * x' },
{ fn: 'x * x * x' },
{ fn: 'x * x * x * x' }
]
}
这边通过设置color也可以设置对应函数图像的颜色
在 function-plot 中表示函数的三种方式:
polyline: 使用内置的采样器来渲染一组不相交的线条
scatter: 虚线
interval: 使用区间算术采样器来渲染一组不相交的矩形
在选项 graphType 中设置要渲染的图形类型(默认为间隔)
{
target: '#f9',
data: [{
fn: '-sqrt(-x)',
nSamples: 100,
graphType: 'scatter'
}, {
fn: 'sqrt(x)',
graphType: 'polyline'
}, {
fn: 'x^2',
graphType: 'interval'
}]
}
鼠标的坐标位置出现小圆圈称为“提示”,可以配置以下选项:
xLine: true 以在尖端位置显示平行于 的虚线
yLine: true 以在尖端位置显示平行于 的虚线
renderer: 提示中显示的文本的自定义渲染函数
注意:提示仅适用于线性函数
当您不希望函数有提示时,请将 skipTip: true 属性添加到保存要呈现的函数信息的对象
{
target: '#f10',
tip: {
xLine: true, // dashed line parallel to y = 0
yLine: true, // dashed line parallel to x = 0
renderer: function (x, y, index) {
// the returning value will be shown in the tip
}
},
yDomain: [-1, 9],
data: [
{ fn: 'x^2' },
{
fn: 'x',
skipTip: true
}
]
}
{
target: '#f11',
data: [{
fn: 'nthRoot(x, 3)^2' // x^(2/3)
}]
}
应当用 nthRoot(x, 3)^2 表示
e的x次幂应使用函数 exp(x)
比如开头的实际上是
{
target: '#test-function',
grid: true,
disableZoom: true,
data: [
{ fn: 'sin(exp(x))', color: 'blue' }
]
}
在两条割线中设置 updateOnMouseMove 的示例,每条线将根据鼠标的当前位置动态计算
{
target: '#f12',
yDomain: [-1, 9],
data: [{
fn: 'x^2',
secants: [{
x0: 2,
updateOnMouseMove: true
}, {
x0: -2,
updateOnMouseMove: true
}]
}]
}
如果 updateOnMouseMove 设置为 true,则每当鼠标在画布内移动时都会计算切线
{
target: '#f13',
yAxis: {domain: [-1, 9]},
data: [{
fn: 'x^2',
derivative: {
fn: '2 * x',
updateOnMouseMove: true
}
}]
}
具有多个函数的图像示例,每个函数都配置有一个导数对象,如上所述自动更新切线
{
target: '#f14',
data: [{
fn: 'x * x',
derivative: {
fn: '2 * x',
updateOnMouseMove: true
}
}, {
fn: 'x * x * x',
derivative: {
fn: '3 * x * x',
updateOnMouseMove: true
}
}]
}
可以链接多个图像,当在原始图形上修改尖端的位置、图形比例或图形转换属性时,链接的图形会使用相同的事件发出信号,在以下示例中,a 触发内部事件,缩放操作在 a 和 b 上执行,但是当 b 触发事件时,缩放操作仅在 b 上执行
// 使用示例
const plotOptionsA = {
target: '#linked-a',
height: 250,
xAxis: { domain: [-10, 10] },
data: [{ fn: 'x * x' }]
};
const plotOptionsB = {
target: '#linked-b',
height: 250,
xAxis: { domain: [-10, 10] },
data: [{ fn: '2 * x' }]
};
// 指定触发配置
const triggerConfig = {
aTriggersB: true,
bTriggersA: false
};
// 调用函数初始化并根据配置链接两个图像
setupLinkedPlots(plotOptionsA, plotOptionsB, triggerConfig);
这一段代码 plotOptionsA 和 plotOptionsB 仍然是一般的定义,但是初始化链接图需要使用 setupLinkedPlots 函数,triggerConfig是核心配置,aTriggersB 为true代表A会影响B,bTriggersA为false代表B不会影响A,如果这个也是true这两个函数图互相影响
在setupLinkedPlots函数外围 不能使用 document.addEventListener(....) 包围
用参数方程来渲染著名的蝴蝶曲线方程,方程是:
{
target: '#f17',
yAxis: {domain: [-4.428571429, 4.428571429]},
xAxis: {domain: [-7, 7]},
data: [{
x: 'sin(t) * (exp(cos(t)) - 2 cos(4t) - sin(t/12)^5)',
y: 'cos(t) * (exp(cos(t)) - 2 cos(4t) - sin(t/12)^5)',
range: [-10 * Math.PI, 10 * Math.PI],
fnType: 'parametric',
graphType: 'polyline'
}]
}
圆 的原方程可以用下面的极坐标方程表示:
其中 是极角, 是圆心 的半径。
告诉 function-plot
呈现极坐标方程的选项在数据数组的每个项中定义,并且需要设置以下属性:
range
属性用于确定 的可能值,记得在属性 samples
中设置样本数Warning
注意:function-plot
默认使用区间算术来创建一条线,而不是区间算术采样器集。使用 graphType: 'polyline'
生成的矩形,它使用正常的单点评估。
{
target: '#f18',
yAxis: {domain: [-1.897959183, 1.897959183]},
xAxis: {domain: [-3, 3]},
data: [{
r: 'r0 * cos(theta - gamma) + sqrt(a^2 - r0^2 * (sin(theta - gamma))^2)',
scope: {
a: 1,
r0: 0,
gamma: 0
},
fnType: 'polar',
graphType: 'polyline'
}]
}
半径为 1 的圆方程 的显式表达方式是:
这个库还可以绘制隐式方程,唯一的要求是将方程设为等于零,并添加选项(采样器期望函数依赖于变量 和 ):
要渲染隐式方程,您必须确保以下几点:
注意:隐式函数只能使用区间算术进行渲染。
{
target: '#f19',
yAxis: { domain: [-1.897959183, 1.897959183] },
xAxis: { domain: [-3, 3] },
data: [
{
fn: 'x * x + y * y - 1',
fnType: 'implicit'
}
]
}
考虑以下方程:
虽然不可能找到它的显式版本,因为我们需要无穷多个函数,但在平面上的有限区域内,有限数量的函数就足够了。
{
target: '#f20',
yAxis: { domain: [-3.795918366, 3.795918366] },
xAxis: { domain: [-6, 6] },
disableZoom: true,
data: [
{
fn: 'cos(PI * x) - cos(PI * y)',
fnType: 'implicit'
}
]
}
要绘制一组点或一条折线,需要以下选项:
points
:一个坐标数组,每个坐标由一个 2 元素数组表示fnType: 'points'
:告诉函数图形库数据已经以点的形式可用请注意,您可以在选项中使用 graphType
的值为 scatter
或 polyline
。
{
target: '#f21',
data: [
{
points: [
[1, 1],
[2, 1],
[2, 2],
[1, 2],
[1, 1]
],
fnType: 'points',
graphType: 'scatter'
}
]
}
{
target: '#f22',
data: [
{
points: [
[1, 1],
[2, 1],
[2, 2],
[1, 2],
[1, 1]
],
fnType: 'points',
graphType: 'polyline'
}
]
}
要渲染 2D 向量,请在每个数据项上设置以下内容:
vector
{Array}:向量本身offset
(可选) {Array}:从原点的位移fnType: 'vector'
:告诉函数图形库数据已经以向量的形式可用graphType: 'polyline'
:渲染从 offset
到 offset + vector
的优美线段{
target: '#f23',
xAxis: { domain: [-3, 8] },
grid: true,
data: [
{
vector: [2, 1],
offset: [1, 2],
graphType: 'polyline',
fnType: 'vector'
}
]
}
要渲染文本,请在每个数据项上设置以下内容:
graphType: 'text'
location
{Array}:文本的位置,一个包含 2 个元素的数组。text
{string}:要显示的文本。{
target: '#f24',
data: [
{
graphType: 'text',
location: [1, 1],
text: 'hello world'
},
{
graphType: 'text',
location: [-1, -1],
text: 'foo bar',
attr: {
'text-anchor': 'end'
}
}
]
}
由于function-plot默认使用区间算术数学,不幸的是,有些函数由于底层的复杂性,尚未实现,因此你也可以使用mathjs (Wikist已经内置)
然后设置以下内容:
sampler: 'builtIn'
与 function-plot
捆绑在一起的解析器将被替换为 在 math.js
graphType: 'polyline'
或graphType: 'scatter'
如这是一个绘制伽马函数的例子
{
target: '#f25',
disableZoom: true,
data: [
{
fn: 'gamma(x)',
sampler: 'builtIn',
graphType: 'polyline'
}
]
}
以上所有示例都使用了字符串作为属性进行评估,例如:
{
data: [{
fn: 'x^2'
}]
}
你可以使用函数代替字符串,输入将根据 fnType 的类型而有所不同。
在任何情况下,输入将是一个单一对象,其属性与函数所依赖的属性相同。例如,当 fnType: 'polar' 时,函数将依赖于 ,那么 将是输入对象中的一个属性(property)。
如果你想使用任何其他绘图器,你的函数预期返回一个单一值(常用)。
如果你想使用区间算术绘图器,你的函数预期返回一个具有 hi
和 lo
属性的对象(很少使用,除非你想与区间算术库进行计算)。
如下两例:
{
target: '#f26',
data: [
{
// force the use of builtIn math
graphType: 'polyline',
fn: function (scope) {
// scope.x = Number
var x = scope.x
return x * x
}
},
{
fnType: 'polar',
graphType: 'polyline',
r: function (scope) {
// scope.theta = number
var r0 = 0
var a = 1
var gamma = 0
return (
r0 * Math.cos(scope.theta - gamma) + Math.sqrt(a * a - r0 * r0 * Math.pow(Math.sin(scope.theta - gamma), 2))
)
}
}
]
}
{
target: '#f27',
data: [
{
// uses interval arithmetic by default
fn: function (scope) {
// scope.x = {lo: Number, hi: number}
// simulate a line e.g. y = x
return {
lo: scope.x.lo,
hi: scope.x.hi
}
}
}
]
}
setupFunctionPlot函数被定义为
setupFunctionPlot(plotOptions, onPlotComplete) {}
其中onPlotComplete是指绘制完成后的响应事件
如
setupFunctionPlot(plotOptions, (plotInstance) => {
console.log('绘图完成:', plotInstance);
});
则会在绘图完成后在控制台打印信息
此处需要你有一定js基础才能使用,适合各种高级定制场景