这是我用于音频播放器的代码:
https://codepen.io/katzkode/pen/ZbxYYG
它的作用:
使用循环为每首歌曲一次创建多个音频播放器(通过使用单个 div 元素)。
循环:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | * create audio elements for each file in files */ function createAudioElements() { for (f in files) { var audioString ="<source src="http://www.alexkatz.me/codepen/music/" + files[f] +""></audio>"; $("#audio-players").append(audioString); } } /* createAudioPlayers * create audio players for each file in files */ function createAudioPlayers() { for (f in files) { var playerString ="<button id="playbutton-" + f +"" class="play playbutton"></button>"; $("#audio-players").append(playerString); } } |
var 文件:
2 3 4 5 | "chooseyourweapon.mp3", // 1 "interlude.mp3", // 2 "scriptures.mp3" // 3 ]; |
在我使用时在 HTML 中调用:
我要完成的工作:
删除循环,以便我可以单独插入任何播放器并单独调整它们但是我想要这样:https://puu.sh/ynD4q/6ab2ba7816.png
我想使用此代码来调用所需的音频播放器:
2 3 4 | </audio> <button id="playbutton-0" class="play playbutton"></button> |
当我尝试使用它时,播放/停止/更改位置按钮停止工作。
这让我发疯了,我刚开始学习 jQuery。非常感谢!
将
2 3 4 5 | {"elementId" :"audio-player-1","src" :"http://www.alexkatz.me/codepen/music/interlude.mp3"}, {"elementId" :"audio-player-2","src" :"http://www.alexkatz.me/codepen/music/chooseyourweapon.mp3"}, {"elementId" :"audio-player-3","src" :"http://www.alexkatz.me/codepen/music/scriptures.mp3"} ]; |
它非常有用,因为你可以添加任何你想要的属性。并且使用
然后在
之后在
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | document.addEventListener("DOMContentLoaded", theDOMHasLoaded, false); // array of audio files (stored in a folder called music) var settings = [{ "elementId":"audio-player-1", "src":"http://www.alexkatz.me/codepen/music/interlude.mp3" }, { "elementId":"audio-player-2", "src":"http://www.alexkatz.me/codepen/music/chooseyourweapon.mp3" }, { "elementId":"audio-player-3", "src":"http://www.alexkatz.me/codepen/music/scriptures.mp3" } ]; /////////////////////////////////////////////// // Find and store audio info /////////////////////////////////////////////// // array for AudioObjects var audioList = []; // components and the index for their AudioObject var componentDict = {}; // store AudioObject that is currently playing var playingAudio = null; // store playhead id if one is being dragged var onplayhead = null; /* AudioObject Constructor */ function AudioObject(audio, duration) { this.audio = audio; this.id = audio.id; this.duration = duration; } /* bindAudioPlayer * Store audioplayer components in correct AudioObject * num identifes correct audioplayer */ AudioObject.prototype.bindAudioPlayer = function(num) { this.audioplayer = document.getElementById("audioplayer-" + num); this.playbutton = document.getElementById("playbutton-" + num); this.timeline = document.getElementById("timeline-" + num); this.playhead = document.getElementById("playhead-" + num); this.timelineWidth = this.timeline.offsetWidth - this.playhead.offsetWidth } /* addEventListeners() */ AudioObject.prototype.addEventListeners = function() { this.audio.addEventListener("timeupdate", AudioObject.prototype.timeUpdate, false); this.audio.addEventListener("durationchange", AudioObject.prototype.durationChange, false); this.timeline.addEventListener("click", AudioObject.prototype.timelineClick, false); this.playbutton.addEventListener("click", AudioObject.prototype.pressPlay, false); // Makes playhead draggable this.playhead.addEventListener('mousedown', AudioObject.prototype.mouseDown, false); window.addEventListener('mouseup', mouseUp, false); } /* populateAudioList */ function populateAudioList() { var audioElements = document.getElementsByClassName("audio"); for (i = 0; i < audioElements.length; i++) { audioList.push( new AudioObject(audioElements[i], 0) ); audioList[i].bindAudioPlayer(i); audioList[i].addEventListeners(); } } /* populateComponentDictionary() * {key=element id : value=index of audioList} */ function populateComponentDictionary() { for (i = 0; i < audioList.length; i++) { componentDict[audioList[i].audio.id] = i; componentDict[audioList[i].playbutton.id] = i; componentDict[audioList[i].timeline.id] = i; componentDict[audioList[i].playhead.id] = i; } } /////////////////////////////////////////////// // Update Audio Player /////////////////////////////////////////////// /* durationChange * set duration for AudioObject */ AudioObject.prototype.durationChange = function() { var ao = audioList[getAudioListIndex(this.id)]; ao.duration = this.duration; } /* pressPlay() * call play() for correct AudioObject */ AudioObject.prototype.pressPlay = function() { var index = getAudioListIndex(this.id); audioList[index].play(); } /* play() * play or pause selected audio, if there is a song playing pause it */ AudioObject.prototype.play = function() { if (this == playingAudio) { playingAudio = null; this.audio.pause(); changeClass(this.playbutton,"playbutton play"); } // else check if playing audio exists and pause it, then start this else { if (playingAudio != null) { playingAudio.audio.pause(); changeClass(playingAudio.playbutton,"playbutton play"); } this.audio.play(); playingAudio = this; changeClass(this.playbutton,"playbutton pause"); } } /* timelineClick() * get timeline's AudioObject */ AudioObject.prototype.timelineClick = function(event) { var ao = audioList[getAudioListIndex(this.id)]; ao.audio.currentTime = ao.audio.duration * clickPercent(event, ao.timeline, ao.timelineWidth); } /* mouseDown */ AudioObject.prototype.mouseDown = function(event) { onplayhead = this.id; var ao = audioList[getAudioListIndex(this.id)]; window.addEventListener('mousemove', AudioObject.prototype.moveplayhead, true); ao.audio.removeEventListener('timeupdate', AudioObject.prototype.timeUpdate, false); } /* mouseUp EventListener * getting input from all mouse clicks */ function mouseUp(e) { if (onplayhead != null) { var ao = audioList[getAudioListIndex(onplayhead)]; window.removeEventListener('mousemove', AudioObject.prototype.moveplayhead, true); // change current time ao.audio.currentTime = ao.audio.duration * clickPercent(e, ao.timeline, ao.timelineWidth); ao.audio.addEventListener('timeupdate', AudioObject.prototype.timeUpdate, false); } onplayhead = null; } /* mousemove EventListener * Moves playhead as user drags */ AudioObject.prototype.moveplayhead = function(e) { var ao = audioList[getAudioListIndex(onplayhead)]; var newMargLeft = e.clientX - getPosition(ao.timeline); if (newMargLeft >= 0 && newMargLeft <= ao.timelineWidth) { document.getElementById(onplayhead).style.marginLeft = newMargLeft +"px"; } if (newMargLeft < 0) { playhead.style.marginLeft ="0px"; } if (newMargLeft > ao.timelineWidth) { playhead.style.marginLeft = ao.timelineWidth +"px"; } } /* timeUpdate * Synchronizes playhead position with current point in audio * this is the html audio element */ AudioObject.prototype.timeUpdate = function() { // audio element's AudioObject var ao = audioList[getAudioListIndex(this.id)]; var playPercent = ao.timelineWidth * (ao.audio.currentTime / ao.duration); ao.playhead.style.marginLeft = playPercent +"px"; // If song is over if (ao.audio.currentTime == ao.duration) { changeClass(ao.playbutton,"playbutton play"); ao.audio.currentTime = 0; ao.audio.pause(); playingAudio = null; } } /////////////////////////////////////////////// // Utility Methods /////////////////////////////////////////////// /* changeClass * overwrites element's class names */ function changeClass(element, newClasses) { element.className = newClasses; } /* getAudioListIndex * Given an element's id, find the index in audioList for the correct AudioObject */ function getAudioListIndex(id) { return componentDict[id]; } /* clickPercent() * returns click as decimal (.77) of the total timelineWidth */ function clickPercent(e, timeline, timelineWidth) { return (event.clientX - getPosition(timeline)) / timelineWidth; } // getPosition // Returns elements left position relative to top-left of viewport function getPosition(el) { return el.getBoundingClientRect().left; } /////////////////////////////////////////////// // GENERATE HTML FOR AUDIO ELEMENTS AND PLAYERS /////////////////////////////////////////////// /* createAudioElements * create audio elements for each file in settings */ function createAudioElements() { for (f in settings) { var audioString ="<source src="" + settings[f].src +""></audio>"; $("#" + settings[f].elementId).append(audioString); } } /* createAudioPlayers * create audio players for each file in settings */ function createAudioPlayers() { for (f in settings) { var playerString ="<button id="playbutton-" + f +"" class="play playbutton">" + "</button>"; $("#" + settings[f].elementId).append(playerString); } } /* theDOMHasLoaded() * Execute when DOM is loaded */ function theDOMHasLoaded(e) { // Generate HTML for audio elements and audio players createAudioElements(); createAudioPlayers(); // Populate Audio List populateAudioList(); populateComponentDictionary(); } |
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | width: 480px; height: 60px; margin: 50px auto auto auto; border: solid; } .playbutton { height: 60px; width: 60px; border: none; float: left; outline: none; } .play { background: url('http://www.alexkatz.me/codepen/img/play.png'); } .pause { background: url('http://www.alexkatz.me/codepen/img/pause.png'); } .play, .pause { background-size: 50% 50%; background-repeat: no-repeat; background-position: center; } .timeline { width: 400px; height: 20px; margin-top: 20px; float: left; border-radius: 15px; background: rgba(0, 0, 0, .3); } .playhead { width: 18px; height: 18px; border-radius: 50%; margin-top: 1px; background: rgba(0, 0, 0, 1); cursor: pointer; } |
2 3 4 5 6 | player 1 player 2 player 3 |
您只需要更改新音频元素的 id,
查看工作演示
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | document.addEventListener("DOMContentLoaded", theDOMHasLoaded, false); // array of audio files (stored in a folder called music) var files = ["interlude.mp3", // 0 "chooseyourweapon.mp3", // 1 "interlude.mp3", // 2 "scriptures.mp3" // 3 ]; /////////////////////////////////////////////// // Find and store audio info /////////////////////////////////////////////// // array for AudioObjects var audioList = []; // components and the index for their AudioObject var componentDict = {}; // store AudioObject that is currently playing var playingAudio = null; // store playhead id if one is being dragged var onplayhead = null; /* AudioObject Constructor */ function AudioObject(audio, duration) { this.audio = audio; this.id = audio.id; this.duration = duration; } /* bindAudioPlayer * Store audioplayer components in correct AudioObject * num identifes correct audioplayer */ AudioObject.prototype.bindAudioPlayer = function(num) { this.audioplayer = document.getElementById("audioplayer-" + num); this.playbutton = document.getElementById("playbutton-" + num); this.timeline = document.getElementById("timeline-" + num); this.playhead = document.getElementById("playhead-" + num); this.timelineWidth = this.timeline.offsetWidth - this.playhead.offsetWidth } /* addEventListeners() */ AudioObject.prototype.addEventListeners = function() { this.audio.addEventListener("timeupdate", AudioObject.prototype.timeUpdate, false); this.audio.addEventListener("durationchange", AudioObject.prototype.durationChange, false); this.timeline.addEventListener("click", AudioObject.prototype.timelineClick, false); this.playbutton.addEventListener("click", AudioObject.prototype.pressPlay, false); // Makes playhead draggable this.playhead.addEventListener('mousedown', AudioObject.prototype.mouseDown, false); window.addEventListener('mouseup', mouseUp, false); } /* populateAudioList */ function populateAudioList() { var audioElements = document.getElementsByClassName("audio"); for (i = 0; i < audioElements.length; i++) { audioList.push( new AudioObject(audioElements[i], 0) ); audioList[i].bindAudioPlayer(i); audioList[i].addEventListeners(); } } /* populateComponentDictionary() * {key=element id : value=index of audioList} */ function populateComponentDictionary() { for (i = 0; i < audioList.length; i++) { componentDict[audioList[i].audio.id] = i; componentDict[audioList[i].playbutton.id] = i; componentDict[audioList[i].timeline.id] = i; componentDict[audioList[i].playhead.id] = i; } } /////////////////////////////////////////////// // Update Audio Player /////////////////////////////////////////////// /* durationChange * set duration for AudioObject */ AudioObject.prototype.durationChange = function() { var ao = audioList[getAudioListIndex(this.id)]; ao.duration = this.duration; } /* pressPlay() * call play() for correct AudioObject */ AudioObject.prototype.pressPlay = function() { var index = getAudioListIndex(this.id); audioList[index].play(); } /* play() * play or pause selected audio, if there is a song playing pause it */ AudioObject.prototype.play = function() { if (this == playingAudio) { playingAudio = null; this.audio.pause(); changeClass(this.playbutton,"playbutton play"); } // else check if playing audio exists and pause it, then start this else { if (playingAudio != null) { playingAudio.audio.pause(); changeClass(playingAudio.playbutton,"playbutton play"); } this.audio.play(); playingAudio = this; changeClass(this.playbutton,"playbutton pause"); } } /* timelineClick() * get timeline's AudioObject */ AudioObject.prototype.timelineClick = function(event) { var ao = audioList[getAudioListIndex(this.id)]; ao.audio.currentTime = ao.audio.duration * clickPercent(event, ao.timeline, ao.timelineWidth); } /* mouseDown */ AudioObject.prototype.mouseDown = function(event) { onplayhead = this.id; var ao = audioList[getAudioListIndex(this.id)]; window.addEventListener('mousemove', AudioObject.prototype.moveplayhead, true); ao.audio.removeEventListener('timeupdate', AudioObject.prototype.timeUpdate, false); } /* mouseUp EventListener * getting input from all mouse clicks */ function mouseUp(e) { if (onplayhead != null) { var ao = audioList[getAudioListIndex(onplayhead)]; window.removeEventListener('mousemove', AudioObject.prototype.moveplayhead, true); // change current time ao.audio.currentTime = ao.audio.duration * clickPercent(e, ao.timeline, ao.timelineWidth); ao.audio.addEventListener('timeupdate', AudioObject.prototype.timeUpdate, false); } onplayhead = null; } /* mousemove EventListener * Moves playhead as user drags */ AudioObject.prototype.moveplayhead = function(e) { var ao = audioList[getAudioListIndex(onplayhead)]; var newMargLeft = e.clientX - getPosition(ao.timeline); if (newMargLeft >= 0 && newMargLeft <= ao.timelineWidth) { document.getElementById(onplayhead).style.marginLeft = newMargLeft +"px"; } if (newMargLeft < 0) { playhead.style.marginLeft ="0px"; } if (newMargLeft > ao.timelineWidth) { playhead.style.marginLeft = ao.timelineWidth +"px"; } } /* timeUpdate * Synchronizes playhead position with current point in audio * this is the html audio element */ AudioObject.prototype.timeUpdate = function() { // audio element's AudioObject var ao = audioList[getAudioListIndex(this.id)]; var playPercent = ao.timelineWidth * (ao.audio.currentTime / ao.duration); ao.playhead.style.marginLeft = playPercent +"px"; // If song is over if (ao.audio.currentTime == ao.duration) { changeClass(ao.playbutton,"playbutton play"); ao.audio.currentTime = 0; ao.audio.pause(); playingAudio = null; } } /////////////////////////////////////////////// // Utility Methods /////////////////////////////////////////////// /* changeClass * overwrites element's class names */ function changeClass(element, newClasses) { element.className = newClasses; } /* getAudioListIndex * Given an element's id, find the index in audioList for the correct AudioObject */ function getAudioListIndex(id) { return componentDict[id]; } /* clickPercent() * returns click as decimal (.77) of the total timelineWidth */ function clickPercent(e, timeline, timelineWidth) { return (event.clientX - getPosition(timeline)) / timelineWidth; } // getPosition // Returns elements left position relative to top-left of viewport function getPosition(el) { return el.getBoundingClientRect().left; } /////////////////////////////////////////////// // GENERATE HTML FOR AUDIO ELEMENTS AND PLAYERS /////////////////////////////////////////////// /* createAudioElements * create audio elements for each file in files */ function createAudioElements() { for (f in files) { var audioString ="<source src="http://www.alexkatz.me/codepen/music/" + files[f] +""></audio>"; $("#audio-players").append(audioString); } } /* createAudioPlayers * create audio players for each file in files */ function createAudioPlayers() { for (f in files) { var playerString ="<button id="playbutton-" + f +"" class="play playbutton"></button>"; $("#audio-players").append(playerString); } } /* theDOMHasLoaded() * Execute when DOM is loaded */ function theDOMHasLoaded(e) { // Generate HTML for audio elements and audio players createAudioElements(); createAudioPlayers(); // Populate Audio List populateAudioList(); populateComponentDictionary(); } |
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | width: 480px; height: 60px; margin: 50px auto auto auto; border: solid; } .playbutton { height: 60px; width: 60px; border: none; float: left; outline: none; } .play { background: url('http://www.alexkatz.me/codepen/img/play.png'); } .pause { background: url('http://www.alexkatz.me/codepen/img/pause.png'); } .play, .pause { background-size: 50% 50%; background-repeat: no-repeat; background-position: center; } .timeline { width: 400px; height: 20px; margin-top: 20px; float: left; border-radius: 15px; background: rgba(0, 0, 0, .3); } .playhead { width: 18px; height: 18px; border-radius: 50%; margin-top: 1px; background: rgba(0, 0, 0, 1); cursor: pointer; } |
2 3 4 5 6 7 | <source src="http://www.alexkatz.me/codepen/music/interlude.mp3"> </audio> <button id="playbutton-4" class="play playbutton"></button> |
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/