带有两个板的 Moodle 公式中的 JSXGraph:绑定到输入字段不起作用
JSXGraph in Moodle Formulas with two boards: Binding to input fields not working
我用两个板创建了运动学领域的 Moodle 公式问题。虽然我只用一块板就可以完美地解决更简单的问题,但这个问题的问题是绑定值没有插入到公式的输入输入字段中。因此,学生无法提交答案,因为实际上没有填写任何内容。问题的其余部分仍然有效,正如在问题预览中填写正确答案时所看到的那样。
我提供了一个 Moodle XML 文件,以便更容易重现问题:questions_formulas_JSXGraph_2boards.xml
您需要安装 JSXGraph
过滤器和问题类型 Formulas
的当前版本的 Moodle。
主要的 JSXGraph 代码是这样的:
<jsxgraph width="400" height="300" numberOfBoards="2" ext_formulas>
// JavaScript code to create the construction.
var jsxCode = function (question) {
// Import final coordinates after submission
var x0={x0};
var t1,t2,t3 , v1,v2,v3 , x1,x2,x3;
[t1,t2,t3 , v1,v2,v3 , x1,x2,x3] =
question.getAllValues([1,2,3 , 1,2,3 , x0,x0,x0 ]);
JXG.Options.point.infoboxDigits = 1;
JXG.Options.point.snapSizeX = 1;
JXG.Options.point.snapSizeY = 0.1;
// Create boards
var brd0 = JXG.JSXGraph.initBoard(BOARDID0, {
boundingbox: [-1, 11, 12, -11], axis:true,
defaultAxes: {
x: {withLabel: true, name: 't in s',
label: {position: 'rt', offset: [-0, 15], anchorX: 'right'} },
y: {withLabel:true, name: 'x in m',
label: {position: 'rt', offset: [+15, -0]} } },
showCopyright: false, showNavigation: false
});
var brd1 = JXG.JSXGraph.initBoard(BOARDID1, {
boundingbox: [-1, 3.5, 12, -3.5], axis:true,
defaultAxes: {
x: {withLabel: true, name: 't in s',
label: {position: 'rt', offset: [-0, 15], anchorX: 'right'} },
y: {withLabel:true, name: 'v_x in m/s',
label: {position: 'rt', offset: [+15, -0]} } },
showCopyright: false, showNavigation: false
});
// Board brd0 needs to be updated when changes in brd1 occur
brd1.addChild(brd0);
// Attributes for points and lines
function attrPfix(addAttr={}) {
const attr = {fixed: true, visible: false, withLabel: false};
return { ...attr, ...addAttr};
}
function attrPmov(addAttr={}) {
const attr = {fixed: question.isSolved, snapToGrid: true, withLabel: false};
return { ...attr, ...addAttr};
}
function attrPsma(addAttr={}) {
const attr = {visible: true, withLabel: false, color:'#4285F4', size: 1};
return { ...attr, ...addAttr};
}
const attrLine = {borders: {strokeColor:'#4285F4', strokeWidth: 3} };
const attrGlid = {visible:false};
// Define lines and points on brd1
brd1.suspendUpdate();
var lV0 = brd1.create('segment', [[0,-10], [0,10]], {visible:false}),
lV3 = brd1.create('segment', [[-10,0], [20,0]], {visible:false});
var pV0 = brd1.create('glider', [0, v1, lV0], attrPmov({name: "pV0"}) ),
pV1 = brd1.create('point', [t1, v2], attrPmov({name: "pV1"}) ),
pV2 = brd1.create('point', [t2, v3], attrPmov({name: "pV2"}) ),
pV3 = brd1.create('glider', [t3, 0, lV3], attrPmov({name: "pV3"}) ),
pV01 = brd1.create('point', ["X(pV1)", "Y(pV0)"], attrPsma() ),
pV12 = brd1.create('point', ["X(pV2)", "Y(pV1)"], attrPsma() ),
pV23 = brd1.create('point', ["X(pV3)", "Y(pV2)"], attrPsma() ) ;
brd1.create('polygonalchain', [ pV0, pV01, pV1, pV12, pV2, pV23, pV3 ], attrLine);
brd1.unsuspendUpdate();
// Define lines and points on brd1
// Q: Is it necessary/beneficial/wrong to suspendUpdate here?
brd0.suspendUpdate();
var lX1 = brd0.create('line', [[function(){return pV1.X();},-10], [function(){return pV1.X();},10]], attrGlid),
lX2 = brd0.create('line', [[function(){return pV2.X();},-10], [function(){return pV2.X();},10]], attrGlid),
lX3 = brd0.create('line', [[function(){return pV3.X();},-10], [function(){return pV3.X();},10]], attrGlid);
var pX0 = brd0.create('point', [0, x0], attrPsma({fixed: true}) ),
pX1 = brd0.create('glider', [t1, x1, lX1], attrPmov({face: 'diamond'}) ),
pX2 = brd0.create('glider', [t2, x2, lX2], attrPmov({face: 'diamond'}) ),
pX3 = brd0.create('glider', [t3, x3, lX3], attrPmov({face: 'diamond'}) );
brd0.create('polygonalchain', [ pX0, pX1, pX2, pX3 ], attrLine);
brd0.unsuspendUpdate();
// Q: Are these updates necessary?
brd0.update();
brd1.update();
// Whenever the construction is altered the values of the points are sent to formulas.
question.bindInput(0, () => { return pV1.X(); });
question.bindInput(1, () => { return PV2.X(); });
question.bindInput(2, () => { return pV3.X(); });
question.bindInput(3, () => { return pV1.Y(); });
question.bindInput(4, () => { return pV2.Y(); });
question.bindInput(5, () => { return PV3.Y(); });
question.bindInput(6, () => { return pX1.Y(); });
question.bindInput(7, () => { return pX2.Y(); });
question.bindInput(8, () => { return pX3.Y(); });
};
// Execute the JavaScript code.
new JSXQuestion(BOARDID0, jsxCode, allowInputEntry=true);
</jsxgraph>
有没有可能是板子id没有交好导致的问题
new JSXQuestion(BOARDID0, jsxCode, allowInputEntry=true);
除了这个问题,我想更好地理解 JSXGraph:
- 是否有可能以某种方式相互安排多个板?即上方、下方、右对齐、居中等
- 将板初始化为“const”或“var”是否有区别?
- 是否necessary/beneficial/wrong在上面的例子中暂停和取消暂停板更新?
- 代码中有手动更新命令吗necessary/beneficial/useless?
- 我在编码或使用 JSXGraph 时是否有任何明显的错误?
事实上,我们的过滤器与公式结合使用时无法在多个板上正常工作是正确的。目前,只有一个板 ID 被传输到 JSXQuestion object,因此它(和公式)对第二个板一无所知。这也是您的示例提出的问题之一。
此外,为了使 bindInput() 方法起作用,实际上必须使用 JSXQuestion.initBoard() 方法对电路板进行初始化。最后,这就是您的示例不起作用的根本问题。
圣诞假期后我将致力于此问题,并将在 1 月份发布新版本的 Moodle 过滤器。也许那时 JSXGraph 也会有一些新东西。
很遗憾,到那时我无法为您提供肮脏的技巧,因为它需要对过滤器进行一些基本更改。
我希望能在一月份告诉你更多。
圣诞快乐,身体健康!
安德烈亚斯
我现在有时间查看您的问题,并且能够扩展 Moodle 过滤器。从新版本v1.1.0-for3.10开始,公式中也支持了几个板子。您可以在此处找到有关如何使用它以及注意事项的详细说明 on GitHub。
新版插件可以在Plugins Directory下载。
我冒昧地修改了上面的示例,它对我有用:
<jsxgraph width="400" height="300" numberOfBoards="2" ext_formulas>
// JavaScript code to create the construction.
var jsxCode = function (question) {
// Import final coordinates after submission
var x0={x0};
var t1,t2,t3 , v1,v2,v3 , x1,x2,x3;
[t1,t2,t3 , v1,v2,v3 , x1,x2,x3] =
question.getAllValues([1,2,3 , 1,2,3 , x0,x0,x0 ]);
JXG.Options.point.infoboxDigits = 1;
JXG.Options.point.snapSizeX = 1;
JXG.Options.point.snapSizeY = 0.1;
// Create boards
var brds = question.initBoards( [
{ // attribs for BOARDID0
boundingbox: [-1, 11, 12, -11], axis:true,
defaultAxes: {
x: {withLabel: true, name: 't in s',
label: {position: 'rt', offset: [-0, 15], anchorX: 'right'} },
y: {withLabel:true, name: 'x in m',
label: {position: 'rt', offset: [+15, -0]} } },
showCopyright: false, showNavigation: false
},
{ // attribs for BOARDID1
boundingbox: [-1, 3.5, 12, -3.5], axis:true,
defaultAxes: {
x: {withLabel: true, name: 't in s',
label: {position: 'rt', offset: [-0, 15], anchorX: 'right'} },
y: {withLabel:true, name: 'v_x in m/s',
label: {position: 'rt', offset: [+15, -0]} } },
showCopyright: false, showNavigation: false
}
] );
var brd0 = brds[0];
var brd1 = brds[1];
console.log(brd0, brd1);
// Board brd0 needs to be updated when changes in brd1 occur
question.addChildsAsc();
/* not needed anymore
brd1.addChild(brd0);
*/
// Attributes for points and lines
function attrPfix(addAttr={}) {
const attr = {fixed: true, visible: false, withLabel: false};
return { ...attr, ...addAttr};
}
function attrPmov(addAttr={}) {
const attr = {fixed: question.isSolved, snapToGrid: true, withLabel: false};
return { ...attr, ...addAttr};
}
function attrPsma(addAttr={}) {
const attr = {visible: true, withLabel: false, color:'#4285F4', size: 1};
return { ...attr, ...addAttr};
}
const attrLine = {borders: {strokeColor:'#4285F4', strokeWidth: 3} };
const attrGlid = {visible:false};
// Define lines and points on brd1
brd1.suspendUpdate();
var lV0 = brd1.create('segment', [[0,-10], [0,10]], {visible:false}),
lV3 = brd1.create('segment', [[-10,0], [20,0]], {visible:false});
var pV0 = brd1.create('glider', [0, v1, lV0], attrPmov({name: "pV0"}) ),
pV1 = brd1.create('point', [t1, v2], attrPmov({name: "pV1"}) ),
pV2 = brd1.create('point', [t2, v3], attrPmov({name: "pV2"}) ),
pV3 = brd1.create('glider', [t3, 0, lV3], attrPmov({name: "pV3"}) ),
pV01 = brd1.create('point', ["X(pV1)", "Y(pV0)"], attrPsma() ),
pV12 = brd1.create('point', ["X(pV2)", "Y(pV1)"], attrPsma() ),
pV23 = brd1.create('point', ["X(pV3)", "Y(pV2)"], attrPsma() ) ;
brd1.create('polygonalchain', [ pV0, pV01, pV1, pV12, pV2, pV23, pV3 ], attrLine);
brd1.unsuspendUpdate();
// Define lines and points on brd1
// Q: Is it necessary/beneficial/wrong to suspendUpdate here?
// A: It can be beneficial if you use a lot of objects. In this case the benefit is not worth mentioning, I think.
brd0.suspendUpdate();
var lX1 = brd0.create('line', [[function(){return pV1.X();},-10], [function(){return pV1.X();},10]], attrGlid),
lX2 = brd0.create('line', [[function(){return pV2.X();},-10], [function(){return pV2.X();},10]], attrGlid),
lX3 = brd0.create('line', [[function(){return pV3.X();},-10], [function(){return pV3.X();},10]], attrGlid);
var pX0 = brd0.create('point', [0, x0], attrPsma({fixed: true}) ),
pX1 = brd0.create('glider', [t1, x1, lX1], attrPmov({face: 'diamond'}) ),
pX2 = brd0.create('glider', [t2, x2, lX2], attrPmov({face: 'diamond'}) ),
pX3 = brd0.create('glider', [t3, x3, lX3], attrPmov({face: 'diamond'}) );
brd0.create('polygonalchain', [ pX0, pX1, pX2, pX3 ], attrLine);
brd0.unsuspendUpdate();
// Q: Are these updates necessary?
/* not with the new version
brd0.update();
brd1.update();
*/
/* not necessary anymore
question.board = brd0;
*/
// Whenever the construction is altered the values of the points are sent to formulas.
question.bindInput(0, () => { return pV1.X(); });
question.bindInput(1, () => { return pV2.X(); }); // typo here
question.bindInput(2, () => { return pV3.X(); });
question.bindInput(3, () => { return pV1.Y(); });
question.bindInput(4, () => { return pV2.Y(); });
question.bindInput(5, () => { return pV3.Y(); }); // typo here
question.bindInput(6, () => { return pX1.Y(); });
question.bindInput(7, () => { return pX2.Y(); });
question.bindInput(8, () => { return pX3.Y(); });
};
// Execute the JavaScript code.
new JSXQuestion(BOARDIDS, jsxCode, allowInputEntry=true); // use BOARDIDS here!!
</jsxgraph>
我已经回答了代码中的其他问题。
希望能帮到你!
你好,安德烈亚斯
为了完整起见,我 post 我的最终版本的 JSXGraph 代码用于基于 Andreas 解决方案的公式问题。我最后的润色是
- 使轴标签使用 LaTeX,
- 使用事件处理程序
.on('drag', ...)
在图表之间进行双向更新,而不是使用 question.addChildsAsc()
。
这是最终代码:
<jsxgraph width="400" height="300" numberOfBoards="2" ext_formulas>
// JavaScript code to create the construction.
var jsxCode = function (question) {
// Import final coordinates after submission
var x0={x0};
var t1,t2,t3 , v1,v2,v3 , x1,x2,x3;
[t1,t2,t3 , v1,v2,v3 , x1,x2,x3] =
question.getAllValues([1,2,3 , 1,2,3 , x0,x0,x0 ]);
JXG.Options.point.infoboxDigits = 1;
JXG.Options.point.snapSizeX = 1;
JXG.Options.point.snapSizeY = 0.1;
// Attributes for points and lines
function attrPfix(addAttr={}) {
const attr = {fixed: true, visible: false, withLabel: false};
return { ...attr, ...addAttr};
}
function attrPmov(addAttr={}) {
const attr = {fixed: question.isSolved, snapToGrid: true, withLabel: false};
return { ...attr, ...addAttr};
}
function attrPsma(addAttr={}) {
const attr = {visible: true, withLabel: false, color:'#4285F4', size: 1};
return { ...attr, ...addAttr};
}
const attrLine = {borders: {strokeColor:'#4285F4', strokeWidth: 3} };
const attrGlid = {visible:false};
// Create boards
var brds = question.initBoards( [
{ // attribs for BOARDID0
boundingbox: [-1, 12, 13, -11], axis:true,
defaultAxes: {
x: {withLabel: true, name: '$$t\;\mathrm{(s)}$$',
label: {position: 'rt', offset: [10, 26], anchorX: 'right', parse: false, fontSize: 12 } },
y: {withLabel:true, name: '$$x\;\mathrm{(m)}$$',
label: {position: 'rt', offset: [10, 15], parse: false, fontSize: 12 } } },
zoom: {enabled:false, wheel: false}, pan: {enabled:false, needTwoFingers: false},
showCopyright: false, showNavigation: false
},
{ // attribs for BOARDID1
boundingbox: [-1, 3.8, 13, -3.5], axis:true,
defaultAxes: {
x: {withLabel: true, name: '$$t\;\mathrm{(s)}$$',
label: {position: 'rt', offset: [10, 26], anchorX: 'right', parse: false, fontSize: 12 } },
y: {withLabel:true, name: '$$v_x\;\mathrm{(m/s)}$$',
label: {position: 'rt', offset: [10, 15], parse: false, fontSize: 12 } } },
zoom: {enabled:false, wheel: false}, pan: {enabled:false, needTwoFingers: false},
showCopyright: false, showNavigation: false
}
] );
var brd0 = brds[0];
var brd1 = brds[1];
// console.log(brd0, brd1);
// Board brd0 needs to be updated when changes in brd1 occur
// question.addChildsAsc();
// Define lines and points on brd1
var pV1 = brd1.create('point', [t1, v1], attrPmov({name: "pV1"}) ),
pV2 = brd1.create('point', [t2, v2], attrPmov({name: "pV2"}) ),
pV3 = brd1.create('point', [t3, v3], attrPmov({name: "pV3"}) ),
pV01 = brd1.create('point', [0, "Y(pV1)"], attrPsma() ),
pV12 = brd1.create('point', ["X(pV1)", "Y(pV2)"], attrPsma() ),
pV23 = brd1.create('point', ["X(pV2)", "Y(pV3)"], attrPsma() ) ;
pV34 = brd1.create('point', ["X(pV3)", 0], attrPsma() ) ;
brd1.create('polygonalchain', [ pV01, pV1, pV12, pV2, pV23, pV3, pV34 ], attrLine);
// Define lines and points on brd0
var pX0 = brd0.create('point', [0, x0], attrPsma({fixed: true}) ),
pX1 = brd0.create('point', [t1, x1], attrPmov({face: 'diamond'}) ),
pX2 = brd0.create('point', [t2, x2], attrPmov({face: 'diamond'}) ),
pX3 = brd0.create('point', [t3, x3], attrPmov({face: 'diamond'}) );
brd0.create('polygonalchain', [ pX0, pX1, pX2, pX3 ], attrLine);
// Define dependencies
pV1.on('drag', function() { pX1.moveTo([this.X(), pX1.Y()], 0); });
pV2.on('drag', function() { pX2.moveTo([this.X(), pX2.Y()], 0); });
pV3.on('drag', function() { pX3.moveTo([this.X(), pX3.Y()], 0); });
pX1.on('drag', function() { pV1.moveTo([this.X(), pV1.Y()], 0); });
pX2.on('drag', function() { pV2.moveTo([this.X(), pV2.Y()], 0); });
pX3.on('drag', function() { pV3.moveTo([this.X(), pV3.Y()], 0); });
// Whenever the construction is altered the values of the points are sent to formulas.
question.bindInput(0, () => { return pV1.X(); });
question.bindInput(1, () => { return pV2.X(); });
question.bindInput(2, () => { return pV3.X(); });
question.bindInput(3, () => { return pV1.Y(); });
question.bindInput(4, () => { return pV2.Y(); });
question.bindInput(5, () => { return pV3.Y(); });
question.bindInput(6, () => { return pX1.Y(); });
question.bindInput(7, () => { return pX2.Y(); });
question.bindInput(8, () => { return pX3.Y(); });
};
// Execute the JavaScript code.
new JSXQuestion(BOARDIDS, jsxCode, allowInputEntry=false); // use BOARDIDS here!!
</jsxgraph>
我用两个板创建了运动学领域的 Moodle 公式问题。虽然我只用一块板就可以完美地解决更简单的问题,但这个问题的问题是绑定值没有插入到公式的输入输入字段中。因此,学生无法提交答案,因为实际上没有填写任何内容。问题的其余部分仍然有效,正如在问题预览中填写正确答案时所看到的那样。
我提供了一个 Moodle XML 文件,以便更容易重现问题:questions_formulas_JSXGraph_2boards.xml
您需要安装 JSXGraph
过滤器和问题类型 Formulas
的当前版本的 Moodle。
主要的 JSXGraph 代码是这样的:
<jsxgraph width="400" height="300" numberOfBoards="2" ext_formulas>
// JavaScript code to create the construction.
var jsxCode = function (question) {
// Import final coordinates after submission
var x0={x0};
var t1,t2,t3 , v1,v2,v3 , x1,x2,x3;
[t1,t2,t3 , v1,v2,v3 , x1,x2,x3] =
question.getAllValues([1,2,3 , 1,2,3 , x0,x0,x0 ]);
JXG.Options.point.infoboxDigits = 1;
JXG.Options.point.snapSizeX = 1;
JXG.Options.point.snapSizeY = 0.1;
// Create boards
var brd0 = JXG.JSXGraph.initBoard(BOARDID0, {
boundingbox: [-1, 11, 12, -11], axis:true,
defaultAxes: {
x: {withLabel: true, name: 't in s',
label: {position: 'rt', offset: [-0, 15], anchorX: 'right'} },
y: {withLabel:true, name: 'x in m',
label: {position: 'rt', offset: [+15, -0]} } },
showCopyright: false, showNavigation: false
});
var brd1 = JXG.JSXGraph.initBoard(BOARDID1, {
boundingbox: [-1, 3.5, 12, -3.5], axis:true,
defaultAxes: {
x: {withLabel: true, name: 't in s',
label: {position: 'rt', offset: [-0, 15], anchorX: 'right'} },
y: {withLabel:true, name: 'v_x in m/s',
label: {position: 'rt', offset: [+15, -0]} } },
showCopyright: false, showNavigation: false
});
// Board brd0 needs to be updated when changes in brd1 occur
brd1.addChild(brd0);
// Attributes for points and lines
function attrPfix(addAttr={}) {
const attr = {fixed: true, visible: false, withLabel: false};
return { ...attr, ...addAttr};
}
function attrPmov(addAttr={}) {
const attr = {fixed: question.isSolved, snapToGrid: true, withLabel: false};
return { ...attr, ...addAttr};
}
function attrPsma(addAttr={}) {
const attr = {visible: true, withLabel: false, color:'#4285F4', size: 1};
return { ...attr, ...addAttr};
}
const attrLine = {borders: {strokeColor:'#4285F4', strokeWidth: 3} };
const attrGlid = {visible:false};
// Define lines and points on brd1
brd1.suspendUpdate();
var lV0 = brd1.create('segment', [[0,-10], [0,10]], {visible:false}),
lV3 = brd1.create('segment', [[-10,0], [20,0]], {visible:false});
var pV0 = brd1.create('glider', [0, v1, lV0], attrPmov({name: "pV0"}) ),
pV1 = brd1.create('point', [t1, v2], attrPmov({name: "pV1"}) ),
pV2 = brd1.create('point', [t2, v3], attrPmov({name: "pV2"}) ),
pV3 = brd1.create('glider', [t3, 0, lV3], attrPmov({name: "pV3"}) ),
pV01 = brd1.create('point', ["X(pV1)", "Y(pV0)"], attrPsma() ),
pV12 = brd1.create('point', ["X(pV2)", "Y(pV1)"], attrPsma() ),
pV23 = brd1.create('point', ["X(pV3)", "Y(pV2)"], attrPsma() ) ;
brd1.create('polygonalchain', [ pV0, pV01, pV1, pV12, pV2, pV23, pV3 ], attrLine);
brd1.unsuspendUpdate();
// Define lines and points on brd1
// Q: Is it necessary/beneficial/wrong to suspendUpdate here?
brd0.suspendUpdate();
var lX1 = brd0.create('line', [[function(){return pV1.X();},-10], [function(){return pV1.X();},10]], attrGlid),
lX2 = brd0.create('line', [[function(){return pV2.X();},-10], [function(){return pV2.X();},10]], attrGlid),
lX3 = brd0.create('line', [[function(){return pV3.X();},-10], [function(){return pV3.X();},10]], attrGlid);
var pX0 = brd0.create('point', [0, x0], attrPsma({fixed: true}) ),
pX1 = brd0.create('glider', [t1, x1, lX1], attrPmov({face: 'diamond'}) ),
pX2 = brd0.create('glider', [t2, x2, lX2], attrPmov({face: 'diamond'}) ),
pX3 = brd0.create('glider', [t3, x3, lX3], attrPmov({face: 'diamond'}) );
brd0.create('polygonalchain', [ pX0, pX1, pX2, pX3 ], attrLine);
brd0.unsuspendUpdate();
// Q: Are these updates necessary?
brd0.update();
brd1.update();
// Whenever the construction is altered the values of the points are sent to formulas.
question.bindInput(0, () => { return pV1.X(); });
question.bindInput(1, () => { return PV2.X(); });
question.bindInput(2, () => { return pV3.X(); });
question.bindInput(3, () => { return pV1.Y(); });
question.bindInput(4, () => { return pV2.Y(); });
question.bindInput(5, () => { return PV3.Y(); });
question.bindInput(6, () => { return pX1.Y(); });
question.bindInput(7, () => { return pX2.Y(); });
question.bindInput(8, () => { return pX3.Y(); });
};
// Execute the JavaScript code.
new JSXQuestion(BOARDID0, jsxCode, allowInputEntry=true);
</jsxgraph>
有没有可能是板子id没有交好导致的问题
new JSXQuestion(BOARDID0, jsxCode, allowInputEntry=true);
除了这个问题,我想更好地理解 JSXGraph:
- 是否有可能以某种方式相互安排多个板?即上方、下方、右对齐、居中等
- 将板初始化为“const”或“var”是否有区别?
- 是否necessary/beneficial/wrong在上面的例子中暂停和取消暂停板更新?
- 代码中有手动更新命令吗necessary/beneficial/useless?
- 我在编码或使用 JSXGraph 时是否有任何明显的错误?
事实上,我们的过滤器与公式结合使用时无法在多个板上正常工作是正确的。目前,只有一个板 ID 被传输到 JSXQuestion object,因此它(和公式)对第二个板一无所知。这也是您的示例提出的问题之一。
此外,为了使 bindInput() 方法起作用,实际上必须使用 JSXQuestion.initBoard() 方法对电路板进行初始化。最后,这就是您的示例不起作用的根本问题。
圣诞假期后我将致力于此问题,并将在 1 月份发布新版本的 Moodle 过滤器。也许那时 JSXGraph 也会有一些新东西。
很遗憾,到那时我无法为您提供肮脏的技巧,因为它需要对过滤器进行一些基本更改。
我希望能在一月份告诉你更多。 圣诞快乐,身体健康!
安德烈亚斯
我现在有时间查看您的问题,并且能够扩展 Moodle 过滤器。从新版本v1.1.0-for3.10开始,公式中也支持了几个板子。您可以在此处找到有关如何使用它以及注意事项的详细说明 on GitHub。
新版插件可以在Plugins Directory下载。
我冒昧地修改了上面的示例,它对我有用:
<jsxgraph width="400" height="300" numberOfBoards="2" ext_formulas>
// JavaScript code to create the construction.
var jsxCode = function (question) {
// Import final coordinates after submission
var x0={x0};
var t1,t2,t3 , v1,v2,v3 , x1,x2,x3;
[t1,t2,t3 , v1,v2,v3 , x1,x2,x3] =
question.getAllValues([1,2,3 , 1,2,3 , x0,x0,x0 ]);
JXG.Options.point.infoboxDigits = 1;
JXG.Options.point.snapSizeX = 1;
JXG.Options.point.snapSizeY = 0.1;
// Create boards
var brds = question.initBoards( [
{ // attribs for BOARDID0
boundingbox: [-1, 11, 12, -11], axis:true,
defaultAxes: {
x: {withLabel: true, name: 't in s',
label: {position: 'rt', offset: [-0, 15], anchorX: 'right'} },
y: {withLabel:true, name: 'x in m',
label: {position: 'rt', offset: [+15, -0]} } },
showCopyright: false, showNavigation: false
},
{ // attribs for BOARDID1
boundingbox: [-1, 3.5, 12, -3.5], axis:true,
defaultAxes: {
x: {withLabel: true, name: 't in s',
label: {position: 'rt', offset: [-0, 15], anchorX: 'right'} },
y: {withLabel:true, name: 'v_x in m/s',
label: {position: 'rt', offset: [+15, -0]} } },
showCopyright: false, showNavigation: false
}
] );
var brd0 = brds[0];
var brd1 = brds[1];
console.log(brd0, brd1);
// Board brd0 needs to be updated when changes in brd1 occur
question.addChildsAsc();
/* not needed anymore
brd1.addChild(brd0);
*/
// Attributes for points and lines
function attrPfix(addAttr={}) {
const attr = {fixed: true, visible: false, withLabel: false};
return { ...attr, ...addAttr};
}
function attrPmov(addAttr={}) {
const attr = {fixed: question.isSolved, snapToGrid: true, withLabel: false};
return { ...attr, ...addAttr};
}
function attrPsma(addAttr={}) {
const attr = {visible: true, withLabel: false, color:'#4285F4', size: 1};
return { ...attr, ...addAttr};
}
const attrLine = {borders: {strokeColor:'#4285F4', strokeWidth: 3} };
const attrGlid = {visible:false};
// Define lines and points on brd1
brd1.suspendUpdate();
var lV0 = brd1.create('segment', [[0,-10], [0,10]], {visible:false}),
lV3 = brd1.create('segment', [[-10,0], [20,0]], {visible:false});
var pV0 = brd1.create('glider', [0, v1, lV0], attrPmov({name: "pV0"}) ),
pV1 = brd1.create('point', [t1, v2], attrPmov({name: "pV1"}) ),
pV2 = brd1.create('point', [t2, v3], attrPmov({name: "pV2"}) ),
pV3 = brd1.create('glider', [t3, 0, lV3], attrPmov({name: "pV3"}) ),
pV01 = brd1.create('point', ["X(pV1)", "Y(pV0)"], attrPsma() ),
pV12 = brd1.create('point', ["X(pV2)", "Y(pV1)"], attrPsma() ),
pV23 = brd1.create('point', ["X(pV3)", "Y(pV2)"], attrPsma() ) ;
brd1.create('polygonalchain', [ pV0, pV01, pV1, pV12, pV2, pV23, pV3 ], attrLine);
brd1.unsuspendUpdate();
// Define lines and points on brd1
// Q: Is it necessary/beneficial/wrong to suspendUpdate here?
// A: It can be beneficial if you use a lot of objects. In this case the benefit is not worth mentioning, I think.
brd0.suspendUpdate();
var lX1 = brd0.create('line', [[function(){return pV1.X();},-10], [function(){return pV1.X();},10]], attrGlid),
lX2 = brd0.create('line', [[function(){return pV2.X();},-10], [function(){return pV2.X();},10]], attrGlid),
lX3 = brd0.create('line', [[function(){return pV3.X();},-10], [function(){return pV3.X();},10]], attrGlid);
var pX0 = brd0.create('point', [0, x0], attrPsma({fixed: true}) ),
pX1 = brd0.create('glider', [t1, x1, lX1], attrPmov({face: 'diamond'}) ),
pX2 = brd0.create('glider', [t2, x2, lX2], attrPmov({face: 'diamond'}) ),
pX3 = brd0.create('glider', [t3, x3, lX3], attrPmov({face: 'diamond'}) );
brd0.create('polygonalchain', [ pX0, pX1, pX2, pX3 ], attrLine);
brd0.unsuspendUpdate();
// Q: Are these updates necessary?
/* not with the new version
brd0.update();
brd1.update();
*/
/* not necessary anymore
question.board = brd0;
*/
// Whenever the construction is altered the values of the points are sent to formulas.
question.bindInput(0, () => { return pV1.X(); });
question.bindInput(1, () => { return pV2.X(); }); // typo here
question.bindInput(2, () => { return pV3.X(); });
question.bindInput(3, () => { return pV1.Y(); });
question.bindInput(4, () => { return pV2.Y(); });
question.bindInput(5, () => { return pV3.Y(); }); // typo here
question.bindInput(6, () => { return pX1.Y(); });
question.bindInput(7, () => { return pX2.Y(); });
question.bindInput(8, () => { return pX3.Y(); });
};
// Execute the JavaScript code.
new JSXQuestion(BOARDIDS, jsxCode, allowInputEntry=true); // use BOARDIDS here!!
</jsxgraph>
我已经回答了代码中的其他问题。
希望能帮到你!
你好,安德烈亚斯
为了完整起见,我 post 我的最终版本的 JSXGraph 代码用于基于 Andreas 解决方案的公式问题。我最后的润色是
- 使轴标签使用 LaTeX,
- 使用事件处理程序
.on('drag', ...)
在图表之间进行双向更新,而不是使用question.addChildsAsc()
。
这是最终代码:
<jsxgraph width="400" height="300" numberOfBoards="2" ext_formulas>
// JavaScript code to create the construction.
var jsxCode = function (question) {
// Import final coordinates after submission
var x0={x0};
var t1,t2,t3 , v1,v2,v3 , x1,x2,x3;
[t1,t2,t3 , v1,v2,v3 , x1,x2,x3] =
question.getAllValues([1,2,3 , 1,2,3 , x0,x0,x0 ]);
JXG.Options.point.infoboxDigits = 1;
JXG.Options.point.snapSizeX = 1;
JXG.Options.point.snapSizeY = 0.1;
// Attributes for points and lines
function attrPfix(addAttr={}) {
const attr = {fixed: true, visible: false, withLabel: false};
return { ...attr, ...addAttr};
}
function attrPmov(addAttr={}) {
const attr = {fixed: question.isSolved, snapToGrid: true, withLabel: false};
return { ...attr, ...addAttr};
}
function attrPsma(addAttr={}) {
const attr = {visible: true, withLabel: false, color:'#4285F4', size: 1};
return { ...attr, ...addAttr};
}
const attrLine = {borders: {strokeColor:'#4285F4', strokeWidth: 3} };
const attrGlid = {visible:false};
// Create boards
var brds = question.initBoards( [
{ // attribs for BOARDID0
boundingbox: [-1, 12, 13, -11], axis:true,
defaultAxes: {
x: {withLabel: true, name: '$$t\;\mathrm{(s)}$$',
label: {position: 'rt', offset: [10, 26], anchorX: 'right', parse: false, fontSize: 12 } },
y: {withLabel:true, name: '$$x\;\mathrm{(m)}$$',
label: {position: 'rt', offset: [10, 15], parse: false, fontSize: 12 } } },
zoom: {enabled:false, wheel: false}, pan: {enabled:false, needTwoFingers: false},
showCopyright: false, showNavigation: false
},
{ // attribs for BOARDID1
boundingbox: [-1, 3.8, 13, -3.5], axis:true,
defaultAxes: {
x: {withLabel: true, name: '$$t\;\mathrm{(s)}$$',
label: {position: 'rt', offset: [10, 26], anchorX: 'right', parse: false, fontSize: 12 } },
y: {withLabel:true, name: '$$v_x\;\mathrm{(m/s)}$$',
label: {position: 'rt', offset: [10, 15], parse: false, fontSize: 12 } } },
zoom: {enabled:false, wheel: false}, pan: {enabled:false, needTwoFingers: false},
showCopyright: false, showNavigation: false
}
] );
var brd0 = brds[0];
var brd1 = brds[1];
// console.log(brd0, brd1);
// Board brd0 needs to be updated when changes in brd1 occur
// question.addChildsAsc();
// Define lines and points on brd1
var pV1 = brd1.create('point', [t1, v1], attrPmov({name: "pV1"}) ),
pV2 = brd1.create('point', [t2, v2], attrPmov({name: "pV2"}) ),
pV3 = brd1.create('point', [t3, v3], attrPmov({name: "pV3"}) ),
pV01 = brd1.create('point', [0, "Y(pV1)"], attrPsma() ),
pV12 = brd1.create('point', ["X(pV1)", "Y(pV2)"], attrPsma() ),
pV23 = brd1.create('point', ["X(pV2)", "Y(pV3)"], attrPsma() ) ;
pV34 = brd1.create('point', ["X(pV3)", 0], attrPsma() ) ;
brd1.create('polygonalchain', [ pV01, pV1, pV12, pV2, pV23, pV3, pV34 ], attrLine);
// Define lines and points on brd0
var pX0 = brd0.create('point', [0, x0], attrPsma({fixed: true}) ),
pX1 = brd0.create('point', [t1, x1], attrPmov({face: 'diamond'}) ),
pX2 = brd0.create('point', [t2, x2], attrPmov({face: 'diamond'}) ),
pX3 = brd0.create('point', [t3, x3], attrPmov({face: 'diamond'}) );
brd0.create('polygonalchain', [ pX0, pX1, pX2, pX3 ], attrLine);
// Define dependencies
pV1.on('drag', function() { pX1.moveTo([this.X(), pX1.Y()], 0); });
pV2.on('drag', function() { pX2.moveTo([this.X(), pX2.Y()], 0); });
pV3.on('drag', function() { pX3.moveTo([this.X(), pX3.Y()], 0); });
pX1.on('drag', function() { pV1.moveTo([this.X(), pV1.Y()], 0); });
pX2.on('drag', function() { pV2.moveTo([this.X(), pV2.Y()], 0); });
pX3.on('drag', function() { pV3.moveTo([this.X(), pV3.Y()], 0); });
// Whenever the construction is altered the values of the points are sent to formulas.
question.bindInput(0, () => { return pV1.X(); });
question.bindInput(1, () => { return pV2.X(); });
question.bindInput(2, () => { return pV3.X(); });
question.bindInput(3, () => { return pV1.Y(); });
question.bindInput(4, () => { return pV2.Y(); });
question.bindInput(5, () => { return pV3.Y(); });
question.bindInput(6, () => { return pX1.Y(); });
question.bindInput(7, () => { return pX2.Y(); });
question.bindInput(8, () => { return pX3.Y(); });
};
// Execute the JavaScript code.
new JSXQuestion(BOARDIDS, jsxCode, allowInputEntry=false); // use BOARDIDS here!!
</jsxgraph>