我在可汗学院的计算机编程课程中开发了我想在可汗学院之外运行的程序。怎么可能呢?
3 回答
可汗学院使用Processing.js<canvas>
,一个用于与元素交互的 JavaScript 库。尽管 Processing 实际上本身就是一门语言,但可汗学院使用仅 JavaScript 的 Processing.js 代码。
因此,您需要设置一个导入 Processing.js 的网页,设置一个<canvas>
,并在画布上构建一个 Processing.js 实例。最后,您需要确保您的可汗学院代码在作用域内包含 Processing.js 实例的所有成员(我使用 执行此操作with
),以及可汗学院对 Processing.js 的一些等价修改,例如mouseIsPressed
和getImage
。
这是一些一直为我工作的样板。可能需要进一步的开发才能使其适用于更复杂的示例;当您发现不起作用的示例时,请发表评论。
<!DOCTYPE html>
<html>
<head>
<title>JavaScript</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/processing.js/1.4.8/processing.min.js"></script>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
var canvas = document.getElementById("canvas");
var processing = new Processing(canvas, function(processing) {
processing.size(400, 400);
processing.background(0xFFF);
var mouseIsPressed = false;
processing.mousePressed = function () { mouseIsPressed = true; };
processing.mouseReleased = function () { mouseIsPressed = false; };
var keyIsPressed = false;
processing.keyPressed = function () { keyIsPressed = true; };
processing.keyReleased = function () { keyIsPressed = false; };
function getImage(s) {
var url = "https://www.kasandbox.org/programming-images/" + s + ".png";
processing.externals.sketch.imageCache.add(url);
return processing.loadImage(url);
}
// use degrees rather than radians in rotate function
var rotateFn = processing.rotate;
processing.rotate = function (angle) {
rotateFn(processing.radians(angle));
};
with (processing) {
// INSERT YOUR KHAN ACADEMY PROGRAM HERE
}
if (typeof draw !== 'undefined') processing.draw = draw;
});
</script>
</body>
</html>
加入罗伯特的回答:
Processing.js 使用弧度作为角度值的默认值,可汗学院 JS 使用度数。如果您在上面的 Robert 代码中添加以下行(在 with 语句之前),那么您可以使用来自 KA 的旋转命令。
var rotateFn = processing.rotate;
processing.rotate = function(angle) {
rotateFn(processing.radians(angle));
}
是的,对不起,我有点迟到了这个问题。但是我自己想通了...
您可以克隆此存储库或按照以下说明进行操作:
或者您可以像这样设置:在您的index.html中将源代码放入所有 3 个文件中:
处理.js:
loadKa.js:
这是将多个单独文件一起加载的生产代码:
!(function(window, JSON, localStorage)
{
function createProcessing()
{
var args = Array.prototype.slice.call(arguments);
args.push({ beginCode: "with(processing)\n{", endCode: "}"});
var any = combine.apply(this, args);
this.cache = window.cache = {};
this.cache.loadedImages = window.cache.loadedImages = {};
this.cache.imageNames = window.cache.imageNames = [
"avatars/aqualine-sapling",
"avatars/aqualine-seed",
"avatars/aqualine-seedling",
"avatars/aqualine-tree",
"avatars/aqualine-ultimate",
"avatars/avatar-team",
"avatars/duskpin-sapling",
"avatars/duskpin-seed",
"avatars/duskpin-tree",
"avatars/duskpin-ultimate",
"avatars/leaf-blue",
"avatars/leaf-green",
"avatars/leaf-grey",
"avatars/leaf-orange",
"avatars/leaf-red",
"avatars/leaf-yellow",
"avatars/leafers-sapling",
"avatars/leafers-seed",
"avatars/leafers-seedling",
"avatars/leafers-tree",
"avatars/leafers-ultimate",
"avatars/marcimus",
"avatars/marcimus-orange",
"avatars/marcimus-purple",
"avatars/marcimus-red",
"avatars/mr-pants",
"avatars/mr-pants-green",
"avatars/mr-pants-orange",
"avatars/mr-pants-pink",
"avatars/mr-pants-purple",
"avatars/mr-pants-with-hat",
"avatars/mr-pink",
"avatars/mr-pink-green",
"avatars/mr-pink-orange",
"avatars/old-spice-man",
"avatars/old-spice-man-blue",
"avatars/orange-juice-squid",
"avatars/piceratops-sapling",
"avatars/piceratops-seed",
"avatars/piceratops-seedling",
"avatars/piceratops-tree",
"avatars/piceratops-ultimate",
"avatars/primosaur-sapling",
"avatars/primosaur-seed",
"avatars/primosaur-seedling",
"avatars/primosaur-tree",
"avatars/primosaur-ultimate",
"avatars/purple-pi",
"avatars/purple-pi-pink",
"avatars/purple-pi-teal",
"avatars/questionmark",
"avatars/robot_female_1",
"avatars/robot_female_2",
"avatars/robot_female_3",
"avatars/robot_male_1",
"avatars/robot_male_2",
"avatars/robot_male_3",
"avatars/spunky-sam",
"avatars/spunky-sam-green",
"avatars/spunky-sam-orange",
"avatars/spunky-sam-red",
"avatars/starky-sapling",
"avatars/starky-seed",
"avatars/starky-seedling",
"avatars/starky-tree",
"avatars/starky-ultimate",
"creatures/Hopper-Happy",
"creatures/Hopper-Cool",
"creatures/Hopper-Jumping",
"creatures/OhNoes",
"creatures/OhNoes-Happy",
"creatures/OhNoes-Hmm",
"cute/Blank",
"cute/BrownBlock",
"cute/CharacterBoy",
"cute/CharacterCatGirl",
"cute/CharacterHornGirl",
"cute/CharacterPinkGirl",
"cute/CharacterPrincessGirl",
"cute/ChestClosed",
"cute/ChestLid",
"cute/ChestOpen",
"cute/DirtBlock",
"cute/DoorTallClosed",
"cute/DoorTallOpen",
"cute/EnemyBug",
"cute/GemBlue",
"cute/GemGreen",
"cute/GemOrange",
"cute/GrassBlock",
"cute/Heart",
"cute/Key",
"cute/PlainBlock",
"cute/RampEast",
"cute/RampWest",
"cute/Rock",
"cute/RoofEast",
"cute/RoofNorth",
"cute/RoofNorthEast",
"cute/RoofNorthWest",
"cute/RoofSouth",
"cute/RoofSouthEast",
"cute/RoofSouthWest",
"cute/RoofWest",
"cute/Selector",
"cute/ShadowEast",
"cute/ShadowNorth",
"cute/ShadowNorthEast",
"cute/ShadowNorthWest",
"cute/ShadowSideWest",
"cute/ShadowSouth",
"cute/ShadowSouthEast",
"cute/ShadowSouthWest",
"cute/ShadowWest",
"cute/WoodBlock",
"cute/Star",
"cute/StoneBlock",
"cute/StoneBlockTall",
"cute/TreeShort",
"cute/TreeTall",
"space/girl2",
"space/girl3",
"space/girl4",
"space/girl5",
"space/healthheart",
"space/minus",
"space/octopus",
"space/planet",
"space/plus",
"space/rocketship",
"space/star",
"space/3",
"space/4",
"space/5",
"space/6",
"space/7",
"space/8",
"space/9"
];
window.links = {
proxyUrl : "https://cors-anywhere.herokuapp.com/",
image : ["https://www.kasandbox.org/third_party/javascript-khansrc/live-editor/build/images/",
"https://github.com/Khan/live-editor/tree/master/images",
"https://www.kasandbox.org/programming-images/"],
};
var self = this;
this.setup = function()
{
function code(processing)
{
processing.size(400, 400);
processing.background(255, 255, 255);
processing.angleMode = "degrees";
processing.mousePressed = function() {};
processing.mouseReleased = function() {};
processing.mouseMoved = function() {};
processing.mouseDragged = function() {};
processing.mouseOver = function() {};
processing.mouseOut = function() {};
processing.keyPressed = function() {};
processing.keyReleased = function() {};
processing.keyTyped = function() {};
processing.getSound = function(name)
{
return "Sound";
};
processing.playSound = function(sound)
{
console.log(sound + " is not supported yet...");
};
processing.getImage = function(name)
{
return (window.cache || self.cache).loadedImages[name] || processing.get(0, 0, 1, 1);
};
var lastGet = processing.get;
processing.get = function()
{
try{
return lastGet.apply(this, arguments);
}
catch(e)
{
if(arguments[2] !== 0 && arguments[3] !== 0)
{
console.log(e);
}else{
throw e;
}
}
};
processing.debug = function(event)
{
try{
return window.console.log.apply(this, arguments);
}
catch(e)
{
processing.println.apply(this, arguments);
}
};
processing.Program = {
restart: function()
{
window.location.reload();
},
assertEqual: function(equiv)
{
if(!equiv)
{
console.warn(equiv);
}
},
};
}
code = combine(new Function("return " + code.toString().split("\n").join(" "))(), any);
var matched = code.toString().match("this[ ]*\[[ ]*\[[ ]*(\"KAInfiniteLoopSetTimeout\")[ ]*\][ ]*\][ ]*\([ ]*\d*[ ]*\);*");
if(matched)
{
code = new Function("return " + code.toString().replace(matched[0], ""))();
}
window.canvas = document.getElementById("canvas");
window.processing = new Processing(canvas, code);
};
this.imageProcessing = new Processing(canvas, function(processing)
{
try{
processing.imageCache = JSON.parse(localStorage.getItem("imageCache"));
}
catch(e)
{
console.log(e);
}
if(!processing.imageCache)
{
processing.imageCache = {};
}
processing.getImage = function(name, callback, url)
{
if(name === undefined) { return get(0, 0, 1, 1); }
url = url || window.links.image[0] + name.split(".")[0] + ".png";
callback = callback || function() {};
if(!processing.imageCache)
{
var img = processing.loadImage(url);
callback(img, name);
return img;
}
if(processing.imageCache[name])
{
var img = processing.loadImage(processing.imageCache[name]);
callback(img, name);
return img;
}
toDataURL(window.links.proxyUrl + url, function(dataUrl)
{
processing.imageCache[name] = dataUrl;
localStorage.setItem("imageCache", JSON.stringify(processing.imageCache));
callback(processing.imageCache[name], name);
});
return processing.loadImage(processing.imageCache[url] || url);
};
window.cache.imageNames.forEach(function(element, index, array)
{
processing.getImage(element, function(img, name)
{
window.cache.loadedImages[name] = img;
if(index === array.length - 1)
{
(window.setTimeout || function(func)
{
return func.apply(this, arguments);
})
(function()
{
self.setup();
}, 50);
}
});
});
});
}
function combine(a, c)
{
var args = Array.prototype.slice.call(arguments);
var config = {};
var funcArgs = "";
var join = "";
for(var i = 0; i < args.length; i++)
{
if(typeof args[i] === "object")
{
config = args[i];
continue;
}
var to = args[i].toString();
var temp = to.substring(to.indexOf('(') + 1, to.indexOf(')'));
if(temp !== "" && temp !== " ")
{
funcArgs += temp + ",";
}
join += to.slice(to.indexOf('{') + 1, -1);
}
funcArgs = funcArgs.slice(0, -1);
return new Function("return function any(" + funcArgs + "){" + (config.beginCode || "").replace("\n", "") + join + (config.endCode || "") + "}")();
}
function toDataURL(url, callback)
{
var xhr = new XMLHttpRequest();
xhr.onload = function()
{
var reader = new FileReader();
reader.onloadend = function()
{
callback(reader.result);
}
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.send();
}
return {
createProcessing: window.createProcessing = this.createProcessing = createProcessing,
toDataURL: window.toDataURL = this.toDataURL = toDataURL,
combine: window.combine = this.combine = combine,
};
}(
(window || {}),
(JSON || { stringify: function() { return "{}"; }, parse: function() { return {}; } }),
(localStorage || { getItem: function() { return {} }, setItem: function() {}, removeItem: function() {} })
));
索引.js:
在您的index.js文件中使用:
function main()
{
//Your code here (Trust me it really works!)
}
createProcessing(main);
You can also add more arguments as functions to the createProcessing function. Yeah I have everything except sounds and sometimes (rarely) hyped up controls!