@puckwang/vue-slot-machine
Version:
A Vue component of a slot machine, made with an HTML5 canvas, RWD.
394 lines (350 loc) • 11.2 kB
JavaScript
import 'konva';
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
}
}
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance");
}
var script = {
props: {
list: {
type: Array,
default: function _default() {
return [{
text: "Apple",
color: "#FF6666",
fontSize: 75,
align: 'center'
}, {
text: "Watermelon",
color: "#66FF8C",
fontSize: 75,
align: 'center'
}, {
text: "Banana",
color: "#FFD966",
fontSize: 60,
align: 'center'
}, {
text: "Grape",
color: "#B366FF",
fontSize: 75,
align: 'center'
}, {
text: "Peach",
color: "#FF66D9",
fontSize: 75,
align: 'center'
}];
}
},
trigger: {
type: Date,
default: null
},
currentIndex: {
type: Number,
default: -1
},
width: {
type: Number,
default: 500
},
height: {
type: Number,
default: 200
},
responsive: {
type: Boolean,
default: false
}
},
data: function data() {
return {
items: [],
stageConfig: {
height: null,
width: null,
centerY: 0
},
itemConfig: {
x: 0,
y: 0,
fontSize: 0,
spacing: 0,
width: 0,
height: 0,
// 產生時所需的高
startY: 0,
// 產生時的起始位置
align: 'center',
wrap: 'none',
ellipsis: true
},
animation: null,
stopping: false,
currentItem: null,
acc: 100,
stage: null,
layer: null
};
},
methods: {
initSetting: function initSetting() {
this.stageConfig.height = this.height;
this.stageConfig.width = this.width;
this.stageConfig.centerY = this.stageConfig.height / 2;
},
refreshTextSetting: function refreshTextSetting() {
this.itemConfig.width = this.stageConfig.width;
this.itemConfig.fontSize = Math.ceil(this.stageConfig.height / 2.5);
this.itemConfig.height = Math.ceil(this.itemConfig.fontSize * 1.2);
this.itemConfig.startY = -this.itemConfig.height * 1.3;
},
initCanvas: function initCanvas() {
this.stage = new Konva.Stage({
container: this.$refs["stage-container"],
width: this.stageConfig.width,
height: this.stageConfig.height
});
this.layer = new Konva.Layer();
this.stage.add(this.layer);
if (this.responsive) {
this.$refs["stage-container"].children[0].style.width = '100%';
this.$refs["stage-container"].children[0].style.height = 'auto';
this.$refs["stage-container"].children[0].style['padding-bottom'] = "".concat(this.stageConfig.height / this.stageConfig.width * 100, "%");
this.$refs["stage-container"].children[0].children[0].style.width = '100%';
this.$refs["stage-container"].children[0].children[0].style.height = '100%';
}
},
drawBorder: function drawBorder() {
this.layer.add(new Konva.Line({
x: 0,
y: 0,
points: [0, this.stageConfig.centerY + this.itemConfig.height / 2, this.stageConfig.width, this.stageConfig.centerY + this.itemConfig.height / 2],
stroke: 'black',
dash: [1],
opacity: 0.5
}));
this.layer.add(new Konva.Line({
x: 0,
y: 0,
points: [0, this.stageConfig.centerY - this.itemConfig.height / 2, this.stageConfig.width, this.stageConfig.centerY - this.itemConfig.height / 2],
stroke: 'black',
dash: [1],
opacity: 0.5
}));
this.layer.add(new Konva.Rect({
x: 0,
y: 0,
width: this.stageConfig.width,
height: this.stageConfig.height,
stroke: 'black',
strokeWidth: 5
}));
this.stage.draw();
},
drawItems: function drawItems() {
var _this = this;
this.items.forEach(function (e) {
e.remove();
});
this.items = [];
var list = this.list.length > 3 ? _toConsumableArray(this.list) : [].concat(_toConsumableArray(this.list), _toConsumableArray(this.list));
list.forEach(function (e, index) {
var text = new Konva.Text(Object.assign(_this.itemConfig, {
text: e.text,
align: e.align || _this.itemConfig.align,
fontFamily: e.fontFamily,
verticalAlign: e.verticalAlign,
fontStyle: e.fontStyle,
fontSize: e.fontSize || _this.itemConfig.fontSize,
fill: e.color || "#000",
y: index * _this.itemConfig.height + _this.itemConfig.startY,
data: e
}));
_this.layer.add(text);
_this.items.push(text);
});
this.stage.draw();
},
drawAll: function drawAll() {
this.layer.children.forEach(function (e) {
return e.remove();
});
this.drawItems();
this.drawBorder();
},
start: function start() {
this.acc = 100;
this.stopping = false;
this.currentItem = this.currentIndex === -1 ? this.items[this.random(0, this.items.length - 1)] : this.items[this.currentIndex];
this.animation.start();
},
complete: function complete() {
this.$emit('onComplete', this.currentItem.attrs.data);
},
moveItems: function moveItems(acc) {
var _this2 = this;
this.items.forEach(function (e) {
var y = (acc + e.y() - _this2.itemConfig.startY) % (_this2.items.length * _this2.itemConfig.height) + _this2.itemConfig.startY;
e.setY(y);
});
},
tweenMoveItems: function tweenMoveItems(distance) {
this.items.forEach(function (e) {
var tween = new Konva.Tween({
node: e,
duration: 0.1,
y: e.y() + distance
});
tween.play();
});
},
createAnimation: function createAnimation() {
var self = this;
this.animation = new Konva.Animation(function () {
self.moveItems(self.acc);
if (!self.stopping && self.acc > 25) {
self.acc = Math.max(self.acc - Math.random(), 25);
} else {
self.stopping = true;
}
if (self.stopping && self.currentItem.y() > 0 && self.currentItem.y() < self.stageConfig.centerY) {
self.tweenMoveItems(self.stageConfig.centerY - (self.currentItem.y() + self.itemConfig.fontSize / 2.5));
self.acc = 0;
self.animation.stop();
self.complete();
} else if (self.stopping && self.currentItem.y() > self.stageConfig.centerY) {
self.acc = Math.max(self.acc - Math.random() % 0.2, 5);
}
}, this.layer);
},
random: function random(min, max) {
return min + Math.floor(Math.random() * (max - min + 1));
}
},
watch: {
trigger: function trigger() {
this.start();
},
list: function list() {
if (this.stage != null) {
this.drawAll();
}
}
},
mounted: function mounted() {
this.initSetting();
this.initCanvas();
this.refreshTextSetting();
this.createAnimation();
this.drawAll();
}
};
function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier
/* server only */
, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) {
if (typeof shadowMode !== 'boolean') {
createInjectorSSR = createInjector;
createInjector = shadowMode;
shadowMode = false;
} // Vue.extend constructor export interop.
var options = typeof script === 'function' ? script.options : script; // render functions
if (template && template.render) {
options.render = template.render;
options.staticRenderFns = template.staticRenderFns;
options._compiled = true; // functional template
if (isFunctionalTemplate) {
options.functional = true;
}
} // scopedId
if (scopeId) {
options._scopeId = scopeId;
}
var hook;
if (moduleIdentifier) {
// server build
hook = function hook(context) {
// 2.3 injection
context = context || // cached call
this.$vnode && this.$vnode.ssrContext || // stateful
this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional
// 2.2 with runInNewContext: true
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
context = __VUE_SSR_CONTEXT__;
} // inject component styles
if (style) {
style.call(this, createInjectorSSR(context));
} // register component module identifier for async chunk inference
if (context && context._registeredComponents) {
context._registeredComponents.add(moduleIdentifier);
}
}; // used by ssr in case component is cached and beforeCreate
// never gets called
options._ssrRegister = hook;
} else if (style) {
hook = shadowMode ? function () {
style.call(this, createInjectorShadow(this.$root.$options.shadowRoot));
} : function (context) {
style.call(this, createInjector(context));
};
}
if (hook) {
if (options.functional) {
// register for functional component in vue file
var originalRender = options.render;
options.render = function renderWithStyleInjection(h, context) {
hook.call(context);
return originalRender(h, context);
};
} else {
// inject component registration as beforeCreate hook
var existing = options.beforeCreate;
options.beforeCreate = existing ? [].concat(existing, hook) : [hook];
}
}
return script;
}
var normalizeComponent_1 = normalizeComponent;
/* script */
const __vue_script__ = script;
/* template */
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{ref:"stage-parent"},[_c('div',{ref:"stage-container"})])};
var __vue_staticRenderFns__ = [];
/* style */
const __vue_inject_styles__ = undefined;
/* scoped */
const __vue_scope_id__ = "data-v-5d8139aa";
/* module identifier */
const __vue_module_identifier__ = undefined;
/* functional template */
const __vue_is_functional_template__ = false;
/* style inject */
/* style inject SSR */
var SlotMachine = normalizeComponent_1(
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
__vue_inject_styles__,
__vue_script__,
__vue_scope_id__,
__vue_is_functional_template__,
__vue_module_identifier__,
undefined,
undefined
);
var index = {
install: function install(Vue, options) {
Vue.component("slot-machine", SlotMachine);
}
};
export default index;
export { SlotMachine };