implement base functionality
This commit is contained in:
parent
1c85a515b4
commit
4c7e9c67f4
@ -1,84 +1,73 @@
|
||||
{
|
||||
"dataRoles": [
|
||||
{
|
||||
"displayName": "Category Data",
|
||||
"name": "category",
|
||||
"kind": "Grouping"
|
||||
},
|
||||
{
|
||||
"displayName": "Measure Data",
|
||||
"name": "measure",
|
||||
"kind": "Measure"
|
||||
}
|
||||
],
|
||||
"dataRoles": [{
|
||||
"displayName": "urls",
|
||||
"name": "urls",
|
||||
"kind": "Grouping",
|
||||
"description": "Urls of pictures, example: 'http://www.example.com/xx.png'"
|
||||
}],
|
||||
"objects": {
|
||||
"dataPoint": {
|
||||
"displayName": "Data colors",
|
||||
"layout": {
|
||||
"displayName": "layout",
|
||||
"properties": {
|
||||
"defaultColor": {
|
||||
"displayName": "Default color",
|
||||
"rowsCount": {
|
||||
"type": {
|
||||
"fill": {
|
||||
"solid": {
|
||||
"color": true
|
||||
"numeric": true
|
||||
},
|
||||
"displayName": "rowsCount",
|
||||
"description": "How many rows?"
|
||||
},
|
||||
"rowGap": {
|
||||
"type": {
|
||||
"numeric": true
|
||||
},
|
||||
"placeHolderText": "{value}%",
|
||||
"displayName": "rowGap",
|
||||
"description": "The distance moved during each cycle.(%)"
|
||||
},
|
||||
"columnGap": {
|
||||
"type": {
|
||||
"numeric": true
|
||||
},
|
||||
"placeHolderText": "msec",
|
||||
"displayName": "columnGap",
|
||||
"description": "How long a cycle takes? (msec)"
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
"animation": {
|
||||
"displayName": "animation",
|
||||
"properties": {
|
||||
"feet": {
|
||||
"type": {
|
||||
"numeric": true
|
||||
},
|
||||
"placeHolderText": "{value}%",
|
||||
"displayName": "feet",
|
||||
"description": "The distance moved during each cycle.(%)"
|
||||
},
|
||||
"interval": {
|
||||
"type": {
|
||||
"numeric": true
|
||||
},
|
||||
"placeHolderText": "numeric",
|
||||
"displayName": "interval",
|
||||
"description": "How long a cycle takes? (msec)"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
"showAllDataPoints": {
|
||||
"displayName": "Show all",
|
||||
"type": {
|
||||
"bool": true
|
||||
}
|
||||
},
|
||||
"fill": {
|
||||
"displayName": "Fill",
|
||||
"type": {
|
||||
"fill": {
|
||||
"solid": {
|
||||
"color": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fillRule": {
|
||||
"displayName": "Color saturation",
|
||||
"type": {
|
||||
"fill": {}
|
||||
}
|
||||
},
|
||||
"fontSize": {
|
||||
"displayName": "Text Size",
|
||||
"type": {
|
||||
"formatting": {
|
||||
"fontSize": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataViewMappings": [
|
||||
{
|
||||
"dataViewMappings": [{
|
||||
"categorical": {
|
||||
"categories": {
|
||||
"for": {
|
||||
"in": "category"
|
||||
"in": "urls"
|
||||
},
|
||||
"dataReductionAlgorithm": {
|
||||
"top": {}
|
||||
}
|
||||
},
|
||||
"values": {
|
||||
"select": [
|
||||
{
|
||||
"bind": {
|
||||
"to": "measure"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
26
pbiviz.json
26
pbiviz.json
@ -1 +1,25 @@
|
||||
{"visual":{"name":"picsScroller","displayName":"picsScroller","guid":"picsScroller4F6E5B21FAB94F2081F33542CFAD9E09","visualClassName":"Visual","version":"1.0.0","description":"","supportUrl":"","gitHubUrl":""},"apiVersion":"2.6.0","author":{"name":"","email":""},"assets":{"icon":"assets/icon.png"},"externalJS":null,"style":"style/visual.less","capabilities":"capabilities.json","dependencies":null,"stringResources":[]}
|
||||
{
|
||||
"visual": {
|
||||
"name": "picsScroller",
|
||||
"displayName": "picsScroller",
|
||||
"guid": "picsScroller4F6E5B21FAB94F2081F33542CFAD9E09",
|
||||
"visualClassName": "Visual",
|
||||
"version": "1.0.0",
|
||||
"description": "scroll pictures",
|
||||
"supportUrl": "https://blog.mujiannan.me",
|
||||
"gitHubUrl": "https://www.github.com/mujiannan"
|
||||
},
|
||||
"apiVersion": "2.6.0",
|
||||
"author": {
|
||||
"name": "shennan",
|
||||
"email": "littlesand@Outlook.com"
|
||||
},
|
||||
"assets": {
|
||||
"icon": "assets/icon.png"
|
||||
},
|
||||
"externalJS": [],
|
||||
"style": "style/visual.less",
|
||||
"capabilities": "capabilities.json",
|
||||
"dependencies": null,
|
||||
"stringResources": []
|
||||
}
|
31
src/css/picsScroller.css
Normal file
31
src/css/picsScroller.css
Normal file
@ -0,0 +1,31 @@
|
||||
div.pics-sroller-inner-container {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.pics-scroller-row-container {
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: nowrap;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
div.pics-scroller-img-container {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.pics-scroll-invisible-img-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
img.pics-scroller-img {
|
||||
height: 100%;
|
||||
}
|
@ -30,19 +30,16 @@ import { dataViewObjectsParser } from "powerbi-visuals-utils-dataviewutils";
|
||||
import DataViewObjectsParser = dataViewObjectsParser.DataViewObjectsParser;
|
||||
|
||||
export class VisualSettings extends DataViewObjectsParser {
|
||||
public dataPoint: dataPointSettings = new dataPointSettings();
|
||||
public animation: AnimationSettings = new AnimationSettings();
|
||||
public layout:LayoutSettings=new LayoutSettings();
|
||||
}
|
||||
|
||||
export class dataPointSettings {
|
||||
// Default color
|
||||
public defaultColor: string = "";
|
||||
// Show all
|
||||
public showAllDataPoints: boolean = true;
|
||||
// Fill
|
||||
public fill: string = "";
|
||||
// Color saturation
|
||||
public fillRule: string = "";
|
||||
// Text Size
|
||||
public fontSize: number = 12;
|
||||
export class AnimationSettings {
|
||||
public feet:number=1;//n% * width
|
||||
public interval:number=20;//ms
|
||||
}
|
||||
export class LayoutSettings{
|
||||
public rowsCount:number=1;
|
||||
public rowGap:number=2;//n% * height
|
||||
public columnGap:number=2;//n% * width
|
||||
}
|
||||
|
||||
|
135
src/visual.ts
135
src/visual.ts
@ -27,6 +27,7 @@
|
||||
|
||||
import "core-js/stable";
|
||||
import "./../style/visual.less";
|
||||
import "./css/picsScroller.css";
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
|
||||
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
|
||||
@ -37,34 +38,130 @@ import DataView = powerbi.DataView;
|
||||
import VisualObjectInstanceEnumerationObject = powerbi.VisualObjectInstanceEnumerationObject;
|
||||
|
||||
import { VisualSettings } from "./settings";
|
||||
import { timeout } from "d3";
|
||||
|
||||
|
||||
export interface IPicsScrollerData {
|
||||
urls: string[];
|
||||
}
|
||||
export class Visual implements IVisual {
|
||||
private target: HTMLElement;
|
||||
private updateCount: number;
|
||||
private container: HTMLElement;
|
||||
private settings: VisualSettings;
|
||||
private textNode: Text;
|
||||
|
||||
private innerContainer!: HTMLDivElement;
|
||||
private animationPlaying: boolean = true;//控制动画暂停
|
||||
constructor(options: VisualConstructorOptions) {
|
||||
console.log('Visual constructor', options);
|
||||
this.target = options.element;
|
||||
this.updateCount = 0;
|
||||
if (document) {
|
||||
const new_p: HTMLElement = document.createElement("p");
|
||||
new_p.appendChild(document.createTextNode("Update count:"));
|
||||
const new_em: HTMLElement = document.createElement("em");
|
||||
this.textNode = document.createTextNode(this.updateCount.toString());
|
||||
new_em.appendChild(this.textNode);
|
||||
new_p.appendChild(new_em);
|
||||
this.target.appendChild(new_p);
|
||||
this.container = options.element;
|
||||
}
|
||||
private initial() {
|
||||
if (this.innerContainer) {
|
||||
this.innerContainer.remove();
|
||||
};
|
||||
this.innerContainer = document.createElement("div");
|
||||
this.innerContainer.className = "pics-sroller-inner-container";
|
||||
this.container.appendChild(this.innerContainer);
|
||||
}
|
||||
|
||||
public update(options: VisualUpdateOptions) {
|
||||
this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);
|
||||
console.log('Visual update', options);
|
||||
if (this.textNode) {
|
||||
this.textNode.textContent = (this.updateCount++).toString();
|
||||
this.initial();
|
||||
|
||||
let data: IPicsScrollerData = { urls: <string[]>options.dataViews[0].categorical.categories[0].values };
|
||||
let feet = options.viewport.width / 100 * this.settings.animation.feet;
|
||||
let interval = this.settings.animation.interval;
|
||||
let rowGap = options.viewport.height / 100 * this.settings.layout.rowGap;
|
||||
let columnGap = options.viewport.width / 100 * this.settings.layout.columnGap;
|
||||
let rowsCount=this.settings.layout.rowsCount;
|
||||
//处理输入
|
||||
if (!(data.urls.length > 0) || !(rowsCount! > 0)) {
|
||||
console.info("picsScroller error: (source_url.length,rowsCount) ", data.urls.length + "_" + rowsCount);
|
||||
return;
|
||||
};
|
||||
if (rowsCount > data.urls.length) {
|
||||
rowsCount = data.urls.length;
|
||||
};
|
||||
let brandsCount = data.urls.length;
|
||||
let unitsCountPerRow = Math.floor(brandsCount / rowsCount);
|
||||
let logoUrlsArr: string[][] = [];
|
||||
|
||||
//转换url列表形态
|
||||
for (let i = 0; i < rowsCount; i++) {
|
||||
logoUrlsArr.push(data.urls.slice(i * unitsCountPerRow, Math.min((i + 1) * unitsCountPerRow, data.urls.length)));
|
||||
};
|
||||
//分成rowsCount行
|
||||
for (let rowNum = 0; rowNum < rowsCount; rowNum++) {
|
||||
let rowContainer = document.createElement('div');
|
||||
this.innerContainer.appendChild(rowContainer);
|
||||
rowContainer.classList.add('pics-scroller-row-container');
|
||||
if (rowNum > 0) {
|
||||
rowContainer.style.marginTop = rowGap + "px";
|
||||
};
|
||||
rowContainer.setAttribute('id', 'pics-scroller-row-container' + rowNum);
|
||||
rowContainer.setAttribute("style", "height:" + 100 / rowsCount + "%;");
|
||||
rowContainer.setAttribute("data-animation-playing", 'false');
|
||||
rowContainer.setAttribute("data-display-placehold", "false");
|
||||
|
||||
//双倍图片容器,第一组用来显示,第二组放上去暂时隐藏
|
||||
//如果第一组发生了溢出,就显示出第二组并应用滚动动画
|
||||
for (let i = 0; i < 2; i++) {
|
||||
for (let j = 0; j < logoUrlsArr[rowNum].length; j++) {
|
||||
let logoContainer = document.createElement('div');
|
||||
logoContainer.className = "pics-scroller-img-container";
|
||||
if (i == 1) {
|
||||
logoContainer.classList.add("pics-scroll-invisible-img-container");
|
||||
};
|
||||
let logo = document.createElement('img');
|
||||
logo.style.margin = "0px " + columnGap / 2 + "px";
|
||||
logo.setAttribute("class", "pics-scroller-img");
|
||||
logo.setAttribute("src", logoUrlsArr[rowNum][j]);
|
||||
logoContainer.appendChild(logo);
|
||||
rowContainer.appendChild(logoContainer);
|
||||
};
|
||||
}
|
||||
|
||||
//重新激活动画
|
||||
if (this.animationPlaying) {
|
||||
this.activateAnimation();
|
||||
}
|
||||
//假无限滚动
|
||||
window.setInterval(this.scrollLeftInfinity, interval, rowContainer, feet);
|
||||
};
|
||||
|
||||
}
|
||||
public activateAnimation() {
|
||||
this.animationPlaying = true;
|
||||
let rowContainers = this.innerContainer.getElementsByClassName("pics-scroller-row-container");
|
||||
for (let i = 0; i < rowContainers.length; i++) {
|
||||
rowContainers[i].setAttribute("data-animation-playing", "true");
|
||||
}
|
||||
}
|
||||
private suspendAnimation() {
|
||||
let rowContainers = this.innerContainer.getElementsByClassName("pics-scroller-row-container");
|
||||
for (let i = 0; i < rowContainers.length; i++) {
|
||||
rowContainers[i].setAttribute("data-animation-playing", "false");
|
||||
}
|
||||
}
|
||||
//定义无限向左滚动函数
|
||||
private scrollLeftInfinity(obj: HTMLDivElement, feet: number) {
|
||||
let playAnimation = <string>obj.getAttribute("data-animation-playing");
|
||||
let displayPlaceHold = <string>obj.getAttribute("data-display-placehold");
|
||||
if (playAnimation == "false") {
|
||||
return;
|
||||
} else if (playAnimation == "true" && displayPlaceHold == "false" && obj.scrollWidth > obj.clientWidth) {
|
||||
let invisibleImgContainers = obj.getElementsByClassName("pics-scroll-invisible-img-container");
|
||||
while (invisibleImgContainers.length > 0) {
|
||||
invisibleImgContainers[0].classList.remove("pics-scroll-invisible-img-container");
|
||||
}
|
||||
obj.setAttribute("data-display-placehold", "true");
|
||||
}
|
||||
if (obj.scrollWidth > obj.clientWidth) {
|
||||
if (obj.scrollLeft >= obj.scrollWidth / 2) {
|
||||
obj.scrollLeft = 0;
|
||||
} else {
|
||||
obj.scrollLeft += feet;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static parseSettings(dataView: DataView): VisualSettings {
|
||||
return <VisualSettings>VisualSettings.parse(dataView);
|
||||
|
Loading…
Reference in New Issue
Block a user